﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading;
using System.Drawing.Drawing2D;
using System.IO;
using System.Xml;
using Excel = Microsoft.Office.Interop.Excel;

namespace YangtzeFilterTestSystem
{
    public partial class ScannerForm : Form
    {     
         private Point rectangleTopLeft;//过滤器起始坐标在框体中的位置（距离左边的距离，距离上面的距离）

        private double kZoomFactor;     //为了将过滤器的轮廓画在Form上，给了一个适当的收缩因子，这个因子是可以调节的，目前设定的大小约为0.8


        private System.Timers.Timer FormTimer;

        private int counts_of_responces;
        private Size rectangleSize;

        private Pen bluePen = new Pen(Color.Blue, 3);
        private Pen redPen = new Pen(Color.Red, 2);
        private string ClassificationToTestResult;
        PointF RealTimeLocationF;      //实时记录当前探头在Form上的绘图位置，决定如何绘制一个扫描块

        //这两个变量用于下游两台粒子计数器同时扫描时的情况。。。。。。
        PointF RealTimePos1;
        PointF RealTimePos2;

        SizeF sizeF;
        RectangleF FilterRect;
        LinearGradientBrush BlockPaintBrush;

        Bitmap bkBitmap;
        Graphics bkGraphics;
        
        private uint FaceScanningXBlockNumber;  //表示一个扫描行有多少个Block，即粒子计数器采集多少次
        private uint FaceScanningYBlockNumber;  //表示要扫描多少行

        private uint FaceScanningXPointNumber;  //这个变量表示在可能渗漏的Block块中扫描探头确认采样的次数

        private uint DownOPC1DataCount;
        private uint DownOPC2DataCount;
        
        //下面两个变量留作备用
        //private uint DownOPC3DataCount;
        //private uint DownOPC4DataCount;

        private uint ScanBlockCount;           //扫描Block计数，这个变量是根据OPC响应次数更新的，它直接反应下游扫描Block的计数。

        //private double ProbeSize;
        private double ProbeAdjStepLength;

        //此变量决定扫描方式
        private byte ScanningMode;

        private byte ScanningStatus; 

        //此变量决定扫描区域，是FIRST_LINE, LAST_LINE, 还是MIDDLE_PARTS. 便于接下来处理
        private byte ScanningRegion;

        //扫描模式定义
        private const byte FRAME_SCANNING_X_DIR123 = 0x01,
                            FRAME_SCANNING_Y_DIR321 = 0x02,
                            FRAME_SCANNING_X_DIR321 = 0x03,
                            FRAME_SCANNING_Y_DIR123 = 0x04,
                            FACE_SCANNING_X_DIR123 = 0x05,
                            FACE_SCANNING_X_DIR321 = 0x06,
                            LEAK_POINT_VERIFY_STATUS = 0x07,
                            IDLE_STATUS = 0x08,
                            SCANNING_STATUS = 0x09;


        private const byte FIRST_LINE = 0x01,
                           LAST_LINE = 0x02,
                           MIDDLE_PARTS = 0x03;


        ScanDataBlock[,] DownScanningData;        //下游扫描原始数据矩阵
        ScanDataBlock[,] UpScanningData;          //上游扫描原始数据矩阵

        System.Data.DataTable table = new System.Data.DataTable("ParentTable");
       
        /**********调试信息相关变量*****************/
        DateTime BeginTime;
        DateTime EndTime;
        Stopwatch sw4MotorRunning;
        Stopwatch sw4ResponseEventDown;
        Stopwatch sw4ResponseEventUp;

        Stopwatch sw4Scanning;

        //String strScanningInfo;
        /**********************************************/

        Thread ScanningThread1;
        Thread ScanningThread2;

        Thread VerifyLeakPointsScanningThread;

        Thread DownOPC1ResponseThread;
        Thread DownOPC2ResponseThread;
        Thread UpOPCResponseThread;

        ScanDataBlock PossibleLeakPoint;

        private Image GreenLEDImage;
        private Image RedLEDImage;

        private System.Timers.Timer ScannerFormUpdateTimer;

        XmlDocument doc = new XmlDocument();  //初始化和保存ScannerForm里面文本框的一些信息

        SaveFileDialog sfdlg = new SaveFileDialog();//用来访问当前保存MSchart的目录

        private string str_mschart_filename;

        private double XScanningLength; //行扫描探头移动的长度

        public ScannerForm()
        {
            InitializeComponent();

            log4net.Config.XmlConfigurator.Configure();

            FormTimer = new System.Timers.Timer();
            FormTimer.AutoReset = true;
            FormTimer.Interval = 1000;
            FormTimer.Elapsed += new System.Timers.ElapsedEventHandler(FormTimer_Elapsed);

            FormTimer.Enabled = true;

            GlobalVariables.RatedFlowrate = 1200;
            GlobalVariables.DesiredFlowrate = GlobalVariables.RatedFlowrate;
            GlobalVariables.NozzleDiameter = Constant.D150_NOZZLE_DIAMETER;
            GlobalVariables.CurrentTestStandard = Constant.EN779_2002;
            GlobalVariables.DustFeeded_cnt = 0;
            GlobalVariables.IsOnTheFirstTime = true;
            GlobalVariables.m_tot = 0;
            GlobalVariables.CurrentTestStandard = Constant.EN779_2002;
            GlobalVariables.Today = DateTime.Now.ToString("yyyy'-'MM'-'dd");
            GlobalVariables.RootDirectory = Application.StartupPath + "\\";
            GlobalVariables.ACS510FormDesiredFrequency = 10.0;
            GlobalVariables.FinalResult.Calculated = false;
            GlobalVariables.ReportLanguage = "CHINESE";

            GlobalVariables.TestAerosol = "DEHS";


            GlobalVariables.DownOPC1RunningCount = 0;
            GlobalVariables.DownOPC2RunningCount = 0;
            GlobalVariables.DownOPC1ResponseEventCount = 0;
            GlobalVariables.DownOPC2ResponseEventCount = 0;

            GlobalVariables.log.Error("Begin logging...");
            GlobalVariables.ApplicationCurrentStatus = "空闲...";
            GlobalVariables.kb = 0.7;
            GlobalVariables.ScanningConfiguration = Constant.ONE_PLUS_ONE;
            GlobalVariables.VersatileVariable = 0;
            GlobalVariables.strDebugInfo = "";

            //初始化硬件设备
            FanDriver.Initilize();
            KIMOSensors.Initilize();
            PLC.Initilize();

            Filter.LocalPenetration = 0.0025; //调试代码！！！2012.04.20加到这里了，添加在初始化中，防止按到上下游粒子计数器对话框时报错

            GlobalVariables.RootDirectory = Application.StartupPath + "\\";
            GreenLEDImage = new Bitmap(GlobalVariables.RootDirectory + "figure\\GreenLED.png");
            RedLEDImage = new Bitmap(GlobalVariables.RootDirectory + "figure\\RedLED.png");

            AlarmPBox.Image = GreenLEDImage;
            
            X_TBox.Text = "100";
            Y_TBox.Text = "100";

            kZoomFactor = 0.8;

            bkBitmap = new Bitmap(Constant.BK_BITMAP_WIDTH, Constant.BK_BITMAP_HEIGHT);
            bkGraphics = Graphics.FromImage((System.Drawing.Image)bkBitmap);

            if (GlobalVariables.ScanningConfiguration == Constant.ONE_PLUS_ONE)
            {
                Dev.DownstreamOPC1.OPCNewDataReadyEvent += new OPCNewDataReadyEventHandler(OPCNewDataReadyEventResponse1);
                Dev.UpstreamOPC.OPCNewDataReadyEvent += new OPCNewDataReadyEventHandler(OPCNewDataReadyEventResponse1);
            }

            if (GlobalVariables.ScanningConfiguration == Constant.ONE_PLUS_TWO)
            {
                Dev.DownstreamOPC1.OPCNewDataReadyEvent += new OPCNewDataReadyEventHandler(DownOPC1NewDataReadyEventResponse2);
                Dev.DownstreamOPC2.OPCNewDataReadyEvent += new OPCNewDataReadyEventHandler(DownOPC2NewDataReadyEventResponse2);

                Dev.UpstreamOPC.OPCNewDataReadyEvent += new OPCNewDataReadyEventHandler(UpOPCNewDataReadyEventResponse2);
            }

            DownScanningData = new ScanDataBlock[FaceScanningYBlockNumber, FaceScanningXBlockNumber];
            UpScanningData = new ScanDataBlock[FaceScanningYBlockNumber, FaceScanningXBlockNumber];


            BeginTime = new DateTime();
            EndTime = new DateTime();

            sw4MotorRunning = new Stopwatch();

            sw4ResponseEventDown = new Stopwatch();

            sw4ResponseEventUp = new Stopwatch();
            sw4ResponseEventUp.Start();

            sw4Scanning = new Stopwatch();

            ProbeAdjStepLengthTBox.Text = "50";

            ProbeAdjStepLength = Convert.ToDouble(ProbeAdjStepLengthTBox.Text);  

            ScanningMode = FACE_SCANNING_X_DIR123;

            ScanningStatus = IDLE_STATUS;

            GlobalVariables.strMPPS = Constant.MPPS_025_030UM;//初始化扫描界面的粒径档为0.25-0.3um
            MPPSTBox.Text = GlobalVariables.strMPPS;//2012.04.22

            PossibleLeakPoint = new ScanDataBlock();

            ScanningDataChart.Series["Upstream"].LegendText = "判漏极限";
            ScanningDataChart.Series["Downstream"].LegendText = "下游局部扫描粒子数";

            VerifyingPBar.Visible = false;

            GlobalVariables.kLeakFactor = 1.0;     //此变量用于数据后处理时

            GlobalVariables.FlowrateRevision = 0.0;

            //FaceScanningXBlockNumber = 9999;   //初始化此变量是为了处理单列扫描时的情形..........................
         }


        public void FormTimer_Elapsed(object source, System.Timers.ElapsedEventArgs e)
        {
            try
            {

                OutT.Text = KIMOSensors.CurrentOutT.ToString() + "℃";
                OutT.ForeColor = Color.Red;

                OutRH.Text = KIMOSensors.CurrentOutRH.ToString() + "% RH";
                OutRH.ForeColor = Color.Green;


                FilterP.Text = KIMOSensors.CurrentFilterP.ToString() + "Pa";
                NozzleP.Text = KIMOSensors.CurrentNozzleP.ToString() + "Pa";

                ActualFreqLabel.Text = FanDriver.GetActualFrequency().ToString();
                ActualFlowrateLabel.Text = (3669.1521 * Math.Pow(GlobalVariables.NozzleDiameter, 2) * Math.Sqrt(KIMOSensors.CurrentNozzleP) + GlobalVariables.FlowrateRevision).ToString("F");

                if (GlobalVariables.FactoryName == "靖江市申达净化设备有限公司")
                {
                    MainFormTitleLabel.Text = "靖江市申达过滤器检测系统";
                }
                if (GlobalVariables.FactoryName == "江苏申达净化设备有限公司")
                {
                    MainFormTitleLabel.Text = "江苏申达过滤器检测系统";
                }
                else
                {
                    MainFormTitleLabel.Text = "靖江市申达过滤器检测系统";
                }


                CurrentXTBox.Text = (Scanner.CurrentX).ToString("N0");
                CurrentYTBox.Text = (Scanner.CurrentY).ToString("N0");
            }
            catch (System.Exception ex)
            {
                GlobalVariables.log.Error("Error in FormTimer_Elaspsed" + ex.ToString());
            }
        }

        public void OPCNewDataReadyEventResponse1(Remote1104 sender, System.EventArgs e)
        {
            try
            {
                if (sender.ID == OPC_ID.DOWNSTREAM_OPC1)
                {
                    GlobalVariables.DownOPC1ResponseEventCount++;

                    ScanDataBlock scanBlock = new ScanDataBlock();

                    sw4ResponseEventDown.Stop();

                    GlobalVariables.strDownOPC1EventInfo += "OPC New Data Ready Event Response @ : " + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";
                    GlobalVariables.strDownOPC1EventInfo += "Time Elapsed: " + sw4ResponseEventDown.ElapsedMilliseconds.ToString() + " million second \n\n";

                    sw4ResponseEventDown.Reset();
                    sw4ResponseEventDown.Start();

                    uint UpstreamOPC_PN_AT_MPPS = 0;
                    uint DownstreamOPC_PN_AT_MPPS = 0;

                    /*
                     * ch1 0.10um--0.15um
                     * ch2 0.15um--0.20um
                     * ch3 0.20um--0.25um
                     * ch4 0.25um--0.30um
                     * ch5 0.30um--0.50um
                     * ch6 0.50um--0.70um
                     * ch7 0.70um--1.00um
                     * ch8 1.00um--INFum
                     */

                    //DownOPC1DataCountTBox.Text = DownOPC1DataCount.ToString();

                    if (GlobalVariables.VersatileVariable == 1)
                    {
                        MessageBox.Show("Before switch(GlobalVariables.strMPPS)......");
                    }

                    switch (GlobalVariables.strMPPS)
                    {
                        case Constant.MPPS_010_015UM:
                            UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch1;
                            DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC1.PN.ch1;
                            break;
                        case Constant.MPPS_015_020UM:
                            UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch2;
                            DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC1.PN.ch2;
                            break;
                        case Constant.MPPS_020_025UM:
                            UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch3;
                            DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC1.PN.ch3;
                            break;
                        case Constant.MPPS_025_030UM:
                            UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch4;
                            DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC1.PN.ch4;
                            break;
                        case Constant.MPPS_030_050UM:
                            UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch5;
                            DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC1.PN.ch5;
                            break;
                        case Constant.MPPS_050_070UM:
                            UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch6;
                            DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC1.PN.ch6;
                            break;
                        case Constant.MPPS_070_100UM:
                            UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch7;
                            DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC1.PN.ch7;
                            break;
                        case Constant.MPPS_100_INFUM:
                            UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch8;
                            DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC1.PN.ch8;
                            break;
                        default:
                            break;
                    }

                    if (GlobalVariables.VersatileVariable == 1)
                    {
                        MessageBox.Show("UpstreamOPC_PN_AT_MPPS = " + UpstreamOPC_PN_AT_MPPS.ToString() + "\n\n" + "DownstreamOPC_PN_AT_MPPS = " + DownstreamOPC_PN_AT_MPPS.ToString());
                        MessageBox.Show("DilutionRation = " + GlobalVariables.DilutionRatio.ToString() + "\n" + "LocalPenetration = " + Filter.LocalPenetration.ToString() + "\n" + "kb = " + GlobalVariables.kb.ToString());

                    }

                    /**************************考虑了泊松分布后的计算***********************************************/

                    double v, y;
                    v = (DownstreamOPC_PN_AT_MPPS + 1) * 2.0;
                    y = 0.025;

                    if (v < 1)
                    {
                        v = 1;
                    }

                    //这里的变量虽然是scanBlock，看起来是Block的数据，事实上定点扫描的时候就是每个探头点的数据了，所以在漏点确认里面也用到了这个变量
                    scanBlock.RawPN = Convert.ToInt32(chisquaredistr.invchisquaredistribution(v, y) / 2);  //考虑了泊松分布后的单次测量粒子数上限值，95%置信区间
                    scanBlock.DownstreamRaw95PN = scanBlock.RawPN;

                    v = (Convert.ToInt32(UpstreamOPC_PN_AT_MPPS * 1 / (GlobalVariables.DilutionRatio)) + 1) * 2.0;
                    y = 0.025;

                    if (v < 1)
                    {
                        v = 1;
                    }

                    scanBlock.UpstreamRaw95PN = Convert.ToInt32(chisquaredistr.invchisquaredistribution(v, y) / 2);  //考虑了泊松分布后的单次测量粒子数上限值，95%置信区间
                      
                    scanBlock.Penetration = Convert.ToDouble(scanBlock.DownstreamRaw95PN) / Convert.ToDouble(scanBlock.UpstreamRaw95PN);
                    scanBlock.Efficiency = 1.0 - scanBlock.Penetration;

                    //GlobalVariables.log.Error("Penetration = " + scanBlock.Penetration.ToString() + "\n" + "Downstream = " + scanBlock.DownstreamRaw95PN.ToString() + 
                    //                           "\n" + "Upstream = " + scanBlock.UpstreamRaw95PN.ToString() + "\n");


                    //特别注意下面表达式中的v可能会出现非数值的错误，原因是DilutionRation, LocalPenetration, and kb没有赋初值，所以引起被零除。
                    v = (UpstreamOPC_PN_AT_MPPS / (GlobalVariables.DilutionRatio) * Filter.LocalPenetration * GlobalVariables.kb) * 2;
                    y = 0.975;

                    if (v < 1)
                    {
                        v = 1;
                    }

                    //这里的变量虽然是scanBlock，看起来是Block的数据，事实上定点扫描的时候就是每个探头点的数据了，所以在漏点确认里面也用到了这个变量
                    scanBlock.ExpectedPN = Convert.ToInt32(chisquaredistr.invchisquaredistribution(v, y) / 2);  //考虑了泊松分布后的单次测量粒子数下限值，95%置信区间

                    /*********************************************************************************/

                    if (GlobalVariables.VersatileVariable == 2)
                    {
                        //MessageBox.Show("RawPN = " + scanBlock.RawPN.ToString() + "\n" + "ExpectedPN = "+scanBlock.ExpectedPN.ToString());
                    }


                    string strRawPN = scanBlock.RawPN.ToString();
                    string strExpectedPN = scanBlock.ExpectedPN.ToString();

                    DownOPC1PNTBox.Text = strRawPN;
                    LeakageLimitPNTBox.Text = strExpectedPN;

                    if (scanBlock.RawPN > scanBlock.ExpectedPN)
                    {
                        AlarmPBox.Image = RedLEDImage;
                    }
                    else
                    {
                        AlarmPBox.Image = GreenLEDImage;
                    }

                    if (ScanningStatus == LEAK_POINT_VERIFY_STATUS)      
                    {
                        PossibleLeakPoint.RawPN = scanBlock.RawPN;
                        PossibleLeakPoint.ExpectedPN = scanBlock.ExpectedPN;
                        PossibleLeakPoint.Penetration = scanBlock.Penetration;
                        PossibleLeakPoint.Efficiency = scanBlock.Efficiency;
                        if (PossibleLeakPoint.RawPN > PossibleLeakPoint.ExpectedPN)
                        {
                            //将确认为漏点的点加入存储链表或数组中，估计还需要一系列的判断
                            //PossibleLeakPoint.Penetration = PossibleLeakPoint.RawPN - PossibleLeakPoint.ExpectedPN;
                            DataTable.VerifiedLeakPoints.Add(PossibleLeakPoint);
                        }

                        DownOPC1DataCount++;//！！！貌似值没有变化啊！！！
                        counts_of_responces++;
                        
                        /*
                        GlobalVariables.strDebugInfo += "漏点确认: BLOCK(" + PossibleLeakPoint.row.ToString() + "," + PossibleLeakPoint.column.ToString() + ")；" + "下游粒子数 = " + PossibleLeakPoint.RawPN.ToString() + "； 判漏极限值 = " + PossibleLeakPoint.ExpectedPN.ToString() + "\r\n";
                        DebugInfoTBox.Text = GlobalVariables.strDebugInfo;

                        DebugInfoTBox.ScrollToCaret();
                        this.DebugInfoTBox.Focus();
                        this.DebugInfoTBox.Select(this.DebugInfoTBox.TextLength, 0);
                        this.DebugInfoTBox.ScrollToCaret();
                         */
                    }          
                    else if (ScanningStatus == IDLE_STATUS)
                    {
                        //Do nothing......
                    }
                    else if (ScanningStatus == SCANNING_STATUS)
                    {
                        GlobalVariables.strScanningProcessingInfo += "Get " + DownOPC1DataCount.ToString() + "th OPCData in this Row AT " 
                            + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";                      

                        scanBlock.row = ScanBlockCount / FaceScanningXBlockNumber;

                        if (ScanningMode == FACE_SCANNING_X_DIR123)
                        {
                            RealTimeLocationF.X += Convert.ToSingle(Probe.ActualVelocity) * Convert.ToSingle(kZoomFactor);
                            sizeF = new SizeF(Convert.ToSingle(Probe.ActualVelocity) * Convert.ToSingle(kZoomFactor), Convert.ToSingle(Probe.bp) * Convert.ToSingle(kZoomFactor));
                            scanBlock.column = ScanBlockCount - scanBlock.row * FaceScanningXBlockNumber;
                        }

                        if (ScanningMode == FACE_SCANNING_X_DIR321)
                        {
                            RealTimeLocationF.X -= Convert.ToSingle(Probe.ActualVelocity) * Convert.ToSingle(kZoomFactor);
                            sizeF = new SizeF(Convert.ToSingle(Probe.ActualVelocity) * Convert.ToSingle(kZoomFactor), Convert.ToSingle(Probe.bp) * Convert.ToSingle(kZoomFactor));
                            scanBlock.column = FaceScanningXBlockNumber - (ScanBlockCount - scanBlock.row * FaceScanningXBlockNumber)-1;
                        }

                        if (scanBlock.row == (FaceScanningYBlockNumber - 1))    //最后一行。。。。。。。。。。
                        {
                            //此处的Constant.REF_POS_Y为过滤器的一个宽边在画布上的Y坐标值，下面这句代码的意思是当扫描到最后一行时，计算确定另一个宽边在画布上的Y坐标值
                            RealTimeLocationF.Y = Convert.ToSingle(Constant.REF_POS_Y - Filter.FoldingWidth * Convert.ToSingle(kZoomFactor));        //根据过滤器的原点位置和其宽度确定这个数值
                        }

                        FilterRect = new RectangleF(RealTimeLocationF, sizeF);
                        BlockPaintBrush = new LinearGradientBrush(FilterRect, Color.Blue, Color.Blue, LinearGradientMode.Horizontal);

                        if (Math.Abs(scanBlock.RawPN - scanBlock.ExpectedPN) / (double)scanBlock.ExpectedPN < 0.15)
                        {
                            BlockPaintBrush = new LinearGradientBrush(FilterRect, Color.Yellow, Color.Yellow, LinearGradientMode.Horizontal);
                            //MessageBox.Show("row = " + scanBlock.row.ToString() + ";" + "column = " + scanBlock.column.ToString());
                            //确认行列的信息已经加入到DataTable.PossibleLeakBlocks里面了。。。。。。
                            DataTable.PossibleLeakBlocks.Add(scanBlock);
                        }
                        else if (((scanBlock.RawPN - scanBlock.ExpectedPN)/(double)scanBlock.ExpectedPN) > 0.15 )
                        {
                            BlockPaintBrush = new LinearGradientBrush(FilterRect, Color.Red, Color.Red, LinearGradientMode.Horizontal);
                            //MessageBox.Show("row = " + scanBlock.row.ToString() + ";" + "column = " + scanBlock.column.ToString());
                            //确认行列的信息已经加入到DataTable.PossibleLeakBlocks里面了。。。。。。
                            DataTable.PossibleLeakBlocks.Add(scanBlock);
                        }
                        else
                        {
                            BlockPaintBrush = new LinearGradientBrush(FilterRect, Color.Blue, Color.Blue, LinearGradientMode.Horizontal);
                        }

                        bkGraphics.FillRectangle(BlockPaintBrush, FilterRect);
                        Invalidate();

                        //将上游和下游粒子数数据实时显示在ScanningDataChart上。。。。。。
                        ScanningDataChart.Series["Downstream"].Points[(int)ScanBlockCount].YValues[0] = scanBlock.RawPN;  //等到下面的上游数据加入序列后一起更新
                        ScanningDataChart.Series["Upstream"].Points[(int)ScanBlockCount].YValues[0] = scanBlock.ExpectedPN;
                        ScanningDataChart.Invalidate();


                        //保存下游扫描数据到数组DownScanningData
                        DownScanningData[scanBlock.row, scanBlock.column] = scanBlock;

                        v = (Convert.ToInt32(UpstreamOPC_PN_AT_MPPS * 1 / (GlobalVariables.DilutionRatio)) + 1) * 2;
                        y = 0.025;

                        if (v < 1)
                        {
                            v = 1;
                        }

                        //这里的变量虽然是scanBlock，看起来是Block的数据，事实上定点扫描的时候就是每个探头点的数据了，所以在漏点确认里面也用到了这个变量
                        scanBlock.RawPN = Convert.ToInt32(chisquaredistr.invchisquaredistribution(v, y) / 2);  //考虑了泊松分布后的单次测量粒子数上限值，95%置信区间
                        UpScanningData[scanBlock.row, scanBlock.column] = scanBlock;

                        ScanBlockCount++;

                        DownOPC1DataCount++;
                        DownOPC1DataCountTBox.Text = DownOPC1DataCount.ToString();
                    }

                }

                if (sender.ID == OPC_ID.UPSTREAM_OPC)
                {

                    //GlobalVariables.strUpstreamOPCEventInfo += DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";

                    sw4ResponseEventUp.Stop();

                    //GlobalVariables.strUpstreamOPCEventInfo += "Time Elapsed: " + sw4ResponseEventUp.ElapsedMilliseconds.ToString() + "\n\n\n";

                    GlobalVariables.strUpOPCEventInfo += sw4ResponseEventUp.ElapsedMilliseconds.ToString() + "\n";

                    sw4ResponseEventUp.Reset();
                    sw4ResponseEventUp.Start();

                    uint UpstreamOPC_PN_AT_MPPS = 0;

                    /*
                     * ch1 0.10um--0.15um
                     * ch2 0.15um--0.20um
                     * ch3 0.20um--0.25um
                     * ch4 0.25um--0.30um
                     * ch5 0.30um--0.50um
                     * ch6 0.50um--0.70um
                     * ch7 0.70um--1.00um
                     * ch8 1.00um--INFum
                     */                    

                    switch (GlobalVariables.strMPPS)
                    {
                        case Constant.MPPS_010_015UM:
                            UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch1;
                            break;
                        case Constant.MPPS_015_020UM:
                            UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch2;
                            break;
                        case Constant.MPPS_020_025UM:
                            UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch3;
                            break;
                        case Constant.MPPS_025_030UM:
                            UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch4;
                            break;
                        case Constant.MPPS_030_050UM:
                            UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch5;
                            break;
                        case Constant.MPPS_050_070UM:
                            UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch6;
                            break;
                        case Constant.MPPS_070_100UM:
                            UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch7;
                            break;
                        case Constant.MPPS_100_INFUM:
                            UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch8;
                            break;
                        default:
                            break;                            
                    }

                    string UpTimestamp = Dev.UpstreamOPC.PN.Timestamp;
                    uint UpCh1 = Dev.UpstreamOPC.PN.ch1;
                    uint UpCh2 = Dev.UpstreamOPC.PN.ch2;
                    uint UpCh3 = Dev.UpstreamOPC.PN.ch3;
                    uint UpCh4 = Dev.UpstreamOPC.PN.ch4;
                    uint UpCh5 = Dev.UpstreamOPC.PN.ch5;
                    uint UpCh6 = Dev.UpstreamOPC.PN.ch6;
                    uint UpCh7 = Dev.UpstreamOPC.PN.ch7;
                    uint UpCh8 = Dev.UpstreamOPC.PN.ch8;

                    //上游的粒子数浓度更新。。。。。。；应该再详细解释下面表达式中的各个数值量。
                    UpConcentrationTBox.Text = (UpstreamOPC_PN_AT_MPPS / (GlobalVariables.DilutionRatio) / (28.3 * 1000 / 60)).ToString("N2");

                    //471.66667计算说明
                    /****************************************************************
                     * 
                     *         28.3*1000 cm^3/(60s) = 471.66667 cm^3/s
                     * 
                     ****************************************************************/
                }
            }
            catch (System.Exception ex)
            {
                GlobalVariables.log.Error("Error occured @ function OPCNewDataReadyEventResponse in ScannerForm.cs" + ex.ToString());
            }
        }

        private void PreparingScanning1()
        {
            GlobalVariables.strTime4FileNameSuffix = DateTime.Now.Year.ToString() + DateTime.Now.Month.ToString() + DateTime.Now.Day.ToString()
                                                    + DateTime.Now.Hour.ToString() + DateTime.Now.Minute.ToString() + DateTime.Now.Second.ToString();

            GlobalVariables.strScanningProcessingInfo = "";
            GlobalVariables.strScanningProcessingInfo += "Preparing Scanning (setting parameters and calculate variables for scanning) \r\n";


            //确保扫描前，OPC都关闭，防止OPC之前关闭后，出现绘画异常。2012.04.22
            Dev.DownstreamOPC1.InstrumentStop();
            Dev.UpstreamOPC.InstrumentStop();


            kZoomFactor = 0.7;

            bkBitmap = new Bitmap(Constant.BK_BITMAP_WIDTH, Constant.BK_BITMAP_HEIGHT);

            bkGraphics = Graphics.FromImage((System.Drawing.Image)bkBitmap);

            /******************绘制过滤器的边框**********************************/
            rectangleSize = new Size((int)((Filter.FoldingLength-Probe.ap) * kZoomFactor), (int)(Filter.FoldingWidth * kZoomFactor));//过滤器边界    ////////////////////////////
            rectangleTopLeft.X = Constant.REF_POS_X;
            rectangleTopLeft.Y = Constant.REF_POS_Y - rectangleSize.Height;

            Rectangle RectangleArea = new Rectangle(rectangleTopLeft, rectangleSize);//过滤器图形示意图
            bkGraphics.DrawRectangle(bluePen, RectangleArea);
            /************************************************************************/

            /***********************计算扫描过程所需要的参数************************/

            XScanningLength = Filter.FoldingLength - Probe.ap;

            FaceScanningXBlockNumber = Convert.ToUInt32(Math.Ceiling(XScanningLength / Probe.SettingVelocity));    //扫描的列数，对应扫描矩阵的列
            Probe.ActualVelocity = 1.0 * XScanningLength / FaceScanningXBlockNumber;         //此为实际的扫描速度，单位为 mm/s
            FaceScanningYBlockNumber = Convert.ToUInt32(Math.Ceiling(Filter.FoldingWidth / Probe.bp));     //扫描的行数，对应扫描矩阵的行
            /************************************************************************/
            
            //调试用信息。。。。。。
            //GlobalVariables.strDebugInfo += "XBlocks = " + FaceScanningXBlockNumber.ToString() + "\r\n" + "Velocity = " + Probe.ActualVelocity.ToString() + "\r\n";
            //DebugInfoTBox.Text = GlobalVariables.strDebugInfo;

            /*************************初始化当前扫描块在内存画布上的位置*****************/
            RealTimeLocationF.X = Constant.REF_POS_X;
            RealTimeLocationF.Y = Constant.REF_POS_Y;
            RealTimeLocationF.X -= Convert.ToSingle(Probe.ActualVelocity) * Convert.ToSingle(kZoomFactor);     //协调扫描Block的坐标。。。。。。
            RealTimeLocationF.Y -= Convert.ToSingle(Probe.bp) * Convert.ToSingle(kZoomFactor);
            /**************************************************************************/

            /******************************初始化扫描数据矩阵*************************************/
            DownScanningData = new ScanDataBlock[FaceScanningYBlockNumber, FaceScanningXBlockNumber];
            UpScanningData = new ScanDataBlock[FaceScanningYBlockNumber, FaceScanningXBlockNumber];
            /**************************************************************************************/

            //这里，只是一个测试而已。。。。。。
            Filter.DownScanningDataTest = new ScanDataBlock[FaceScanningYBlockNumber, FaceScanningXBlockNumber];

            //清空之前的可能的Leak Blocks!
            DataTable.PossibleLeakBlocks.Clear();

            /**************************ScanBlockCount计数******************************************/
            ScanBlockCount = 0;

            /*************************调试信息******************************/
            GlobalVariables.DownOPC1RunningCount = 0;
            GlobalVariables.DownOPC1ResponseEventCount = 0;

            GlobalVariables.strMotorRunningInfo = "";
            Dev.DownstreamOPC1.strOPCRunningInfo = "";

            GlobalVariables.strDownOPC1EventInfo = "";
            GlobalVariables.strDownOPC2EventInfo = "";

            GlobalVariables.strUpOPCEventInfo = "";
            /***************************************************************/


            ScanningMode = FACE_SCANNING_X_DIR123;

            /*
            //X_DIR123和X_DIR321的选择主要是根据当前扫描探头所在的位置
            if (ScanningModeCBox.SelectedItem.ToString() == "X_DIR123")
            {
                ScanningMode = FACE_SCANNING_X_DIR123;
            }
            else if (ScanningModeCBox.SelectedItem.ToString() == "X_DIR321")
            {
                ScanningMode = FACE_SCANNING_X_DIR321;
            }
             */

            ScanningDataChart.Series["Upstream"].Points.Clear();
            ScanningDataChart.Series["Downstream"].Points.Clear();
            ScanningDataChart.Series["Criterion"].Points.Clear();

            if (Filter.Type=="H13")
            {
                ScanningDataChart.ChartAreas[0].AxisY.Maximum = 3000;
            }
            else if (Filter.Type == "H14")
            {
                ScanningDataChart.ChartAreas[0].AxisY.Maximum = 3000;
            }
            else if (Filter.Type == "U15")
            {
                ScanningDataChart.ChartAreas[0].AxisY.Maximum = 1000;
            }
            else if (Filter.Type == "U16")
            {
                ScanningDataChart.ChartAreas[0].AxisY.Maximum = 300;
            }
            else if (Filter.Type == "U17")
            {
                ScanningDataChart.ChartAreas[0].AxisY.Maximum = 200;
            }
            else
            {
                ScanningDataChart.ChartAreas[0].AxisY.Maximum = 5000;
            }
            
            
            //ScanningDataChart.ChartAreas[0].AxisY.Maximum = 5000;

            for (int row = 0; row < FaceScanningYBlockNumber; row++)
            {
                for (int col = 0; col < FaceScanningXBlockNumber; col++)
                {
                    ScanningDataChart.Series["Upstream"].Points.AddY(100.0);
                    ScanningDataChart.Series["Upstream"].LegendText = "判漏极限";
                    ScanningDataChart.Series["Downstream"].Points.AddY(0.0);
                    ScanningDataChart.Series["Downstream"].LegendText = "下游局部扫描粒子数";
                    ScanningDataChart.Series["Criterion"].Points.AddY(0.0);
                }
            }

            ScanningDataChart.Series["Upstream"].Color = Color.Red;
            ScanningDataChart.Series["Downstream"].Color = Color.Blue;
            ScanningDataChart.Series["Criterion"].Color = Color.Green;

            ScanningDataChart.Invalidate();     //强制Chart图像重绘当前所有数据点

            Invalidate();

            //扫描过程中的信息......
            GlobalVariables.strScanningProcessingInfo += "\r\n\r\n\r\n";
            GlobalVariables.strScanningProcessingInfo += "Filter Dimensions \r\n";
            GlobalVariables.strScanningProcessingInfo += "Filter Length: " + Filter.FoldingLength.ToString() + "\r\n";
            GlobalVariables.strScanningProcessingInfo += "Filter Width: " + Filter.FoldingWidth.ToString() + "\r\n";
            GlobalVariables.strScanningProcessingInfo += "\r\n\r\n\r\n";

            GlobalVariables.strScanningProcessingInfo += "Scanning Row & Column\r\n";
            GlobalVariables.strScanningProcessingInfo += "Scanning Row: " + FaceScanningYBlockNumber.ToString() + "\r\n";
            GlobalVariables.strScanningProcessingInfo += "Scanning Column: " + FaceScanningXBlockNumber.ToString() + "\r\n";

            GlobalVariables.strScanningProcessingInfo += "\r\n\r\n\r\n";

            //DrawAxis
            DrawXAxis(bkGraphics);
            DrawYAxis(bkGraphics);
        }

        private void Scanning1()
        {

            try
            {

                ScanningStatus = SCANNING_STATUS; //切换到扫描状态下
                
                GlobalVariables.strScanningProcessingInfo += "Scanner Initialize......\r\n";
                Scanner.Initialize();

                GlobalVariables.strScanningProcessingInfo += "Preparing OPCs of Upstream and Downstream......\r\n";

                GlobalVariables.strScanningProcessingInfo += "Start Upstream OPC......\r\n";
                Dev.UpstreamOPC.SimpleConfigure();
                Thread.Sleep(1000);
                Dev.UpstreamOPC.InstrumentStart();

                Dev.DownstreamOPC1.SimpleConfigure();
                Thread.Sleep(1000);

                GlobalVariables.strMotorRunningInfo = "";

                //扫描块计数，这个变量用于标识所扫描的Block的计数，这个变量跟随探头和OPC的计数扫描而变化。。。。。。
                ScanBlockCount = 0;

                int i;
                ScanningDataChart.ChartAreas[0].AxisX.MajorGrid.Interval = FaceScanningXBlockNumber;//每个行显示一个Grid，区分行与行。
                GlobalVariables.strScanningProcessingInfo += "Starting Scanning......\r\n";
                
                sw4Scanning.Start();

                GlobalVariables.strScanningProcessingInfo += "Scanner Start AT : " + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";

                //此处赋值只是为了初始化，在接下来的if语句里才是真正的赋值......
                byte dir = Constant.DIR123;

                if (ScanningMode == FACE_SCANNING_X_DIR123)
                {
                    dir = Constant.DIR123;

                    GlobalVariables.strScanningProcessingInfo += "起始扫描模式为FORWARD，即DIR123...\r\n";
                }

                if (ScanningMode == FACE_SCANNING_X_DIR321)
                {
                    dir = Constant.DIR321;

                    RealTimeLocationF.X = Constant.REF_POS_X + (float)(Filter.FoldingLength * kZoomFactor);
                    RealTimeLocationF.Y = Constant.REF_POS_Y;
                    RealTimeLocationF.Y -= Convert.ToSingle(Probe.bp) * Convert.ToSingle(kZoomFactor);

                    GlobalVariables.strScanningProcessingInfo += "起始扫描模式为BACKWARD，即DIR321...\r\n";
                }

                if (FaceScanningYBlockNumber == 1)   //单行扫描处理
                {
                    GlobalVariables.strScanningProcessingInfo += "处理整个扫描过程只有1行的情况......\r\n";    //尽管这种情况极少发生，但是还得有相应的处理代码

                    i = 0;

                    DownOPC1DataCount = 0;   //用于同步粒子计数器的计数次数和伺服电机运行的步调

                    GlobalVariables.strScanningProcessingInfo += "启动下游粒子计数器......\r\n";
                    Dev.DownstreamOPC1.PredefinedSampleCount = Convert.ToInt32(FaceScanningXBlockNumber);
                    Dev.DownstreamOPC1.InstrumentStart();

                    /*****************************************************************************************************
                     为了记录实际的OPC数据采集发生的间隔，此句主要用来检查错误用。这句是每行开始的一个触发，接下来的记录和触发在                   
                     事件响应函数OPCNewDataReadyEventResponse中，最后的停止在一行扫描完成之后还有一句sw4ResponseEventDown.Stop();
                     ******************************************************************************************************/
                    sw4ResponseEventDown.Start();

                    /********************下面这部分代码是扫描一行，主要语句为Scanneer.MoveX(......)，其它是一些调试用信息语句************/
                    GlobalVariables.strMotorRunningInfo += "X_Begin: " + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\r\n";

                    sw4MotorRunning.Reset();
                    sw4MotorRunning.Start();

                    GlobalVariables.strScanningProcessingInfo += "Starting " + (2 * i + 1).ToString() + "th ROW scanning AT "
                         + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\r\n";

                    Scanner.MoveX(XScanningLength, Probe.ActualVelocity, dir);//应该是步进，滤芯X方向长度-探头ap,2012.04.21,又改回去了.改动太大暂时放弃了，设置下//应该是步进，滤芯X方向长度-探头ap,2012.04.21

                    //X方向走完了，FaceScanningXBlockNumber也记录好了，所以要停掉秒表了。 为了记录实际的OPC数据采集发生的间隔
                    sw4ResponseEventDown.Stop();

                    //此while循环是是同步X方向行扫描和粒子计数器数据采集的，保证粒子计数器采集到足够的次数
                    while (DownOPC1DataCount != FaceScanningXBlockNumber)
                    {
                        GlobalVariables.strScanningProcessingInfo += "Waiting for ROW scanning finish...... \r\n ";
                        Thread.Sleep(100);
                    }



                    GlobalVariables.strScanningProcessingInfo += "Stop " + (2 * i + 1).ToString() + "th ROW scanning AT "
                         + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\r\n";

                    GlobalVariables.strScanningProcessingInfo += "\r\n\r\n\r\n";

                    sw4MotorRunning.Stop();

                    GlobalVariables.strMotorRunningInfo += "X_End : " + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\r\n";
                    GlobalVariables.strMotorRunningInfo += "Time Elapsed: " + sw4MotorRunning.ElapsedMilliseconds.ToString() + " ms\r\n";
                    GlobalVariables.strMotorRunningInfo += (2 * i + 1).ToString() + "\r\n";
                    GlobalVariables.strMotorRunningInfo += "\r\n\r\n\r\n";
                    /*****************************************************************************************************************/

                    Thread.Sleep(100);
                }

                for (i = 0; i < FaceScanningYBlockNumber / 2; i++)
                {
                    DownOPC1DataCount = 0;   //用于同步粒子计数器的计数次数和伺服电机运行的步调

                    GlobalVariables.strScanningProcessingInfo += "启动下游粒子计数器......\r\n";
                    Dev.DownstreamOPC1.PredefinedSampleCount = Convert.ToInt32(FaceScanningXBlockNumber);
                    Dev.DownstreamOPC1.InstrumentStart();

                    /*****************************************************************************************************
                     为了记录实际的OPC数据采集发生的间隔，此句主要用来检查错误用。这句是每行开始的一个触发，接下来的记录和触发在                   
                     事件响应函数OPCNewDataReadyEventResponse中，最后的停止在一行扫描完成之后还有一句sw4ResponseEventDown.Stop();
                     ******************************************************************************************************/
                    sw4ResponseEventDown.Start();

                    /********************下面这部分代码是扫描一行，主要语句为Scanneer.MoveX(......)，其它是一些调试用信息语句************/
                    GlobalVariables.strMotorRunningInfo += "X_Begin: " + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";

                    sw4MotorRunning.Reset();
                    sw4MotorRunning.Start();

                    GlobalVariables.strScanningProcessingInfo += "Starting " + (2 * i + 1).ToString() + "th ROW scanning AT "
                         + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";

                    Scanner.MoveX(XScanningLength, Probe.ActualVelocity, dir);//应该是步进，滤芯X方向长度-探头ap,2012.04.21,又改回去了.改动太大暂时放弃了，设置下//应该是步进，滤芯X方向长度-探头ap,2012.04.21       //扫描一行............

                    //X方向走完了，FaceScanningXBlockNumber也记录好了，所以要停掉秒表了。 为了记录实际的OPC数据采集发生的间隔
                    sw4ResponseEventDown.Stop();

                    //此while循环是是同步X方向行扫描和粒子计数器数据采集的，保证粒子计数器采集到足够的次数
                    while (DownOPC1DataCount != FaceScanningXBlockNumber)
                    {
                        GlobalVariables.strScanningProcessingInfo += "Waiting for ROW scanning finish...... \n ";
                        Thread.Sleep(100);
                    }

                    GlobalVariables.strScanningProcessingInfo += "Stop " + (2 * i + 1).ToString() + "th ROW scanning AT "
                         + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";

                    GlobalVariables.strScanningProcessingInfo += "\n\n\n";

                    sw4MotorRunning.Stop();

                    GlobalVariables.strMotorRunningInfo += "X_End : " + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";
                    GlobalVariables.strMotorRunningInfo += "Time Elapsed: " + sw4MotorRunning.ElapsedMilliseconds.ToString() + " ms\n";
                    GlobalVariables.strMotorRunningInfo += (2 * i + 1).ToString() + "\n";
                    GlobalVariables.strMotorRunningInfo += "\n\n\n";
                    /****************************************************************************************************************/

                    Thread.Sleep(100);

                    if ((FaceScanningYBlockNumber % 2 != 0) || ((FaceScanningYBlockNumber % 2 == 0) && (i != (FaceScanningYBlockNumber / 2 - 1))))  
                    {
                        //Y向步进，因为是第一次，直接按照ProbeSize的大小步进是没有问题的。。。。。。
                        Scanner.MoveY(Probe.bp, Probe.ActualVelocity, Constant.DIR321);

                        //Y向步进后协调Block块的坐标，这是为了绘图做准备的。。。。。。。。。。。。。
                        RealTimeLocationF.Y -= Convert.ToSingle(Probe.bp * kZoomFactor);

                        if (ScanningMode == FACE_SCANNING_X_DIR123)
                        {
                            RealTimeLocationF.X += Convert.ToSingle(Probe.ActualVelocity * kZoomFactor);
                        }
                        else if (ScanningMode == FACE_SCANNING_X_DIR321)
                        {
                            RealTimeLocationF.X -= Convert.ToSingle(Probe.ActualVelocity * kZoomFactor);
                        }

                        //调整参数进行接下来一行的扫描
                        DownOPC1DataCount = 0;

                        if (ScanningMode == FACE_SCANNING_X_DIR123)
                        {
                            dir = Constant.DIR321;
                            ScanningMode = FACE_SCANNING_X_DIR321;
                        }
                        else if (ScanningMode == FACE_SCANNING_X_DIR321)
                        {
                            dir = Constant.DIR123;
                            ScanningMode = FACE_SCANNING_X_DIR123;
                        }

                        GlobalVariables.strScanningProcessingInfo += "启动下游粒子计数器......\n";
                        Dev.DownstreamOPC1.PredefinedSampleCount = Convert.ToInt32(FaceScanningXBlockNumber);
                        Dev.DownstreamOPC1.InstrumentStart();

                        /*****************************************************************************************************
                         为了记录实际的OPC数据采集发生的间隔，此句主要用来检查错误用。这句是每行开始的一个触发，接下来的记录和触发在                   
                         事件响应函数OPCNewDataReadyEventResponse中，最后的停止在一行扫描完成之后还有一句sw4ResponseEventDown.Stop();
                         ******************************************************************************************************/

                        sw4ResponseEventDown.Start();

                        /********************下面这部分代码是扫描一行，主要语句为Scanneer.MoveX(......)，其它是一些调试用信息语句************/
                        GlobalVariables.strMotorRunningInfo += "X_End: " + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";

                        sw4MotorRunning.Reset();
                        sw4MotorRunning.Start();

                        GlobalVariables.strScanningProcessingInfo += "Starting " + (2 * i + 2).ToString() + "th ROW scanning AT "
                             + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";

                        Scanner.MoveX(XScanningLength, Probe.ActualVelocity, dir);//应该是步进，滤芯X方向长度-探头ap,2012.04.21,又改回去了.改动太大暂时放弃了，设置下//应该是步进，滤芯X方向长度-探头ap,2012.04.21

                        sw4MotorRunning.Stop();

                        //此while循环是是同步X方向行扫描和粒子计数器数据采集的，保证粒子计数器采集到足够的次数
                        while (DownOPC1DataCount != FaceScanningXBlockNumber)
                        {
                            GlobalVariables.strScanningProcessingInfo += "Waiting for ROW scanning finish...... \n ";
                            Thread.Sleep(100);
                        }

                        GlobalVariables.strScanningProcessingInfo += "Stop " + (2 * i + 2).ToString() + "th ROW scanning AT "
                             + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";

                        GlobalVariables.strScanningProcessingInfo += "\n\n";

                        GlobalVariables.strMotorRunningInfo += "X_Begin: " + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";
                        GlobalVariables.strMotorRunningInfo += "Time Elapsed: " + sw4MotorRunning.ElapsedMilliseconds.ToString() + " ms \n";
                        GlobalVariables.strMotorRunningInfo += (2 * i + 2).ToString() + "\n";
                        GlobalVariables.strMotorRunningInfo += "\n\n\n";
                        /****************************************************************************************************************/

                        //X方向走完了，FaceScanningXBlockNumber也记录好了，所以要停掉秒表了。 为了记录实际的OPC数据采集发生的间隔
                        sw4ResponseEventDown.Stop();

                        Thread.Sleep(100);
                    }

                    if (i == (FaceScanningYBlockNumber / 2 - 1))
                    {
                            GlobalVariables.strScanningProcessingInfo += "处理最后一行Y向步进......\n";

                            //Y向步进，步进距离是算出来的 （此为最后一次Y向步进），只在最后一次的时候会出现ProbeSize的问题。。。。
                            Scanner.MoveY(Filter.FoldingWidth - Probe.bp * (FaceScanningYBlockNumber - 1), Probe.ActualVelocity, Constant.DIR321);

                            //Y向步进后协调Block块的坐标，这是为了绘图做准备的。。。。。。。。。。。。。
                            RealTimeLocationF.Y -= Convert.ToSingle(Probe.bp * kZoomFactor); //这个好像放这里是有问题的，但是，没关系，在画图的事件处理里面重新处理了坐标20120420

                            if (ScanningMode == FACE_SCANNING_X_DIR123)
                            {
                                RealTimeLocationF.X += Convert.ToSingle(Probe.ActualVelocity * kZoomFactor);
                            }
                            else if (ScanningMode == FACE_SCANNING_X_DIR321)
                            {
                                RealTimeLocationF.X -= Convert.ToSingle(Probe.ActualVelocity * kZoomFactor);
                            }


                            DownOPC1DataCount = 0;

                            if (ScanningMode == FACE_SCANNING_X_DIR123)
                            {
                                dir = Constant.DIR321;
                                ScanningMode = FACE_SCANNING_X_DIR321;
                            }
                            else if (ScanningMode == FACE_SCANNING_X_DIR321)
                            {
                                dir = Constant.DIR123;
                                ScanningMode = FACE_SCANNING_X_DIR123;
                            }

                            GlobalVariables.strScanningProcessingInfo += "启动下游粒子计数器......\n";
                            Dev.DownstreamOPC1.PredefinedSampleCount = Convert.ToInt32(FaceScanningXBlockNumber);
                            Dev.DownstreamOPC1.InstrumentStart();
                            sw4ResponseEventDown.Start();   //为了记录实际的OPC数据采集发生的间隔

                            GlobalVariables.strMotorRunningInfo += "X_Begin: " + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";

                            sw4MotorRunning.Reset();
                            sw4MotorRunning.Start();

                            GlobalVariables.strScanningProcessingInfo += "Starting " + (2 * i + 1).ToString() + "th ROW scanning AT "
                                + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";

                            Scanner.MoveX(XScanningLength, Probe.ActualVelocity, dir);//应该是步进，滤芯X方向长度-探头ap,2012.04.21,又改回去了.改动太大暂时放弃了，设置下//应该是步进，滤芯X方向长度-探头ap,2012.04.21

                            sw4MotorRunning.Stop();

                            while (DownOPC1DataCount != FaceScanningXBlockNumber)
                            {
                                GlobalVariables.strScanningProcessingInfo += "Waiting for ROW scanning finish...... \n ";
                                Thread.Sleep(100);
                            }

                            GlobalVariables.strScanningProcessingInfo += "Stop " + (2 * i + 1).ToString() + "th ROW scanning AT "
                                + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";

                            GlobalVariables.strMotorRunningInfo += "X_End: " + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";
                            GlobalVariables.strMotorRunningInfo += "Time Elapsed: " + sw4MotorRunning.ElapsedMilliseconds.ToString() + " ms \n";
                            GlobalVariables.strMotorRunningInfo += (2 * i + 1).ToString() + "\n";
                            GlobalVariables.strMotorRunningInfo += "\n\n\n";

                            Thread.Sleep(100);
                    }
                    else    //如果不是最后一次步进的话，先Y向步进，再扫描，然后直接在Y方向移动一个探头的大小
                    {
                        GlobalVariables.strScanningProcessingInfo += "扫描探头在Y向步进......\n";
                        Scanner.MoveY(Probe.bp, Probe.ActualVelocity, Constant.DIR321);

                        RealTimeLocationF.Y -= Convert.ToSingle(Probe.bp * kZoomFactor);

                        if (ScanningMode == FACE_SCANNING_X_DIR123)
                        {
                            RealTimeLocationF.X += Convert.ToSingle(Probe.ActualVelocity * kZoomFactor);
                        }
                        else if (ScanningMode == FACE_SCANNING_X_DIR321)
                        {
                            RealTimeLocationF.X -= Convert.ToSingle(Probe.ActualVelocity * kZoomFactor);
                        }
                    }

                    if (ScanningMode == FACE_SCANNING_X_DIR123)
                    {
                        dir = Constant.DIR321;
                        ScanningMode = FACE_SCANNING_X_DIR321;
                    }
                    else if (ScanningMode == FACE_SCANNING_X_DIR321)
                    {
                        dir = Constant.DIR123;
                        ScanningMode = FACE_SCANNING_X_DIR123;
                    }
                }

                sw4Scanning.Stop();

                ScanningStatus = IDLE_STATUS; //切换到空闲状态下

                /***************************扫描过程中的所有信息记录，最后都保存在文件中，只有扫描完成了才能得到这些文件****************************************/
                GlobalVariables.strScanningProcessingInfo += "Scanner Stop AT: " + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";
                GlobalVariables.strScanningProcessingInfo += "Finish Scanning......\n";
                GlobalVariables.strScanningProcessingInfo += "\n\n\n";

                GlobalVariables.strScanningProcessingInfo += "Total Scanning Time: " + sw4Scanning.ElapsedMilliseconds / 1000 + " second \n";
                GlobalVariables.strScanningProcessingInfo += "Scanning Velocity: " + Probe.ActualVelocity + " mm/s\n";

                GlobalVariables.strScanningProcessingInfo += "Total OPC Data Count: " + GlobalVariables.DownOPC1ResponseEventCount.ToString() + "\n";

                GlobalVariables.strDownOPC1EventInfo = "Down OPC1 Event Response Total Times: " + GlobalVariables.DownOPC1ResponseEventCount.ToString() + "\n\n\n" + GlobalVariables.strDownOPC1EventInfo;

                Dev.DownstreamOPC1.strOPCRunningInfo = "Down OPC1 Running Times: " + GlobalVariables.DownOPC1RunningCount.ToString() + "\n\n\n" + Dev.DownstreamOPC1.strOPCRunningInfo;

                File.WriteAllText("MotorRunningInfo" + GlobalVariables.strTime4FileNameSuffix + ".txt", GlobalVariables.strMotorRunningInfo);
                File.WriteAllText("DownOPC1RunningInfo" + GlobalVariables.strTime4FileNameSuffix + ".txt", Dev.DownstreamOPC1.strOPCRunningInfo);
                File.WriteAllText("DownOPC1ResponseEvent" + GlobalVariables.strTime4FileNameSuffix + ".txt", GlobalVariables.strDownOPC1EventInfo);
                File.WriteAllText("UpOPCResponseEvent" + GlobalVariables.strTime4FileNameSuffix + ".txt", GlobalVariables.strUpOPCEventInfo);
                File.WriteAllText("ScanningProcessingInfo" + GlobalVariables.strTime4FileNameSuffix + ".txt", GlobalVariables.strScanningProcessingInfo);    //扫描过程中的所有信息都保存在这个文件中......             
                /******************************************************************************************************************************************/

                //此处曾引起过IO异常，有时间的话好好看看。。。。。。。。。这个地方有错会导致它后面的程序代码不能正常执行，所以把它放在了最后面。
                Dev.UpstreamOPC.InstrumentStop();
                Thread.Sleep(1000);          

                //将可能的漏点输出到Text文档中来
                string strPossibleLeakBlocks = "";
                string strTime = DateTime.Now.Hour.ToString() + "H" + DateTime.Now.Minute.ToString() + "M" + DateTime.Now.Second.ToString() + "S";

                strPossibleLeakBlocks += "Possible Leak Blocks\r\n\r\n\r\n";

                strPossibleLeakBlocks += "Total Possible Leak Blocks are " + DataTable.PossibleLeakBlocks.Count.ToString() + "\r\n\r\n";

                foreach (ScanDataBlock leakBlock in DataTable.PossibleLeakBlocks)
                {
                    strPossibleLeakBlocks += "X, Y =  Block(" + leakBlock.row.ToString() + "," + leakBlock.column.ToString() + ")\r\n\r\n\r\n";
                }

                string fileName = "PossibleLeakBlocks" + strTime + ".txt";
                File.WriteAllText(fileName, strPossibleLeakBlocks);

                System.Diagnostics.Process.Start(fileName);
            }

            catch (System.Exception ex)
            {
                GlobalVariables.log.Error("Error ocured @ function Scanning() in ScannerForm.cs" + ex.ToString());
            }
        }

        //此region内部的代码是为ONE_PLUS_TWO写的，到2011.12.22为止，由于复杂度，不想再继续走这条路了，
        //这些已经写过的代码就在里面，不去管它了，如果哪一天还能用得上的话再把它们找出来
        #region
        public void DownOPC1NewDataReadyEventResponse2(Remote1104 sender, System.EventArgs e)
        {
            DownOPC1ResponseThread = new Thread(DownOPC1Response2);
            DownOPC1ResponseThread.Start();
            //DownOPC1Response = new Thread(D
        }

        public void DownOPC1Response2()
        {
            try
            {
                //sender.ID == OPC_ID.DOWNSTREAM_OPC1

                #region
                //if (sender.ID == OPC_ID.DOWNSTREAM_OPC1)
                {
                    if (ScanningRegion == FIRST_LINE)
                    {

                        //MessageBox.Show("In first line mode......");

                        GlobalVariables.DownOPC1ResponseEventCount++;

                        ScanDataBlock scanBlock = new ScanDataBlock();

                        sw4ResponseEventDown.Stop();

                        GlobalVariables.strDownOPC1EventInfo += "OPC New Data Ready Event Response @ : " + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";
                        GlobalVariables.strDownOPC1EventInfo += "Time Elapsed: " + sw4ResponseEventDown.ElapsedMilliseconds.ToString() + " million second \n\n";

                        sw4ResponseEventDown.Reset();

                        sw4ResponseEventDown.Start();

                        GlobalVariables.strScanningProcessingInfo += "Get " + DownOPC1DataCount.ToString() + "th OPCData in this Row AT "
                                + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";

                        scanBlock.row = ScanBlockCount / FaceScanningXBlockNumber;

                        if (ScanningMode == FACE_SCANNING_X_DIR123)
                        {
                            RealTimePos1.X += Convert.ToSingle(Probe.ActualVelocity) * Convert.ToSingle(kZoomFactor);
                            sizeF = new SizeF(Convert.ToSingle(Probe.ActualVelocity) * Convert.ToSingle(kZoomFactor), Convert.ToSingle(Probe.bp) * Convert.ToSingle(kZoomFactor));
                            scanBlock.column = ScanBlockCount - scanBlock.row * FaceScanningXBlockNumber;
                        }

                        if (ScanningMode == FACE_SCANNING_X_DIR321)
                        {
                            RealTimePos1.X -= Convert.ToSingle(Probe.ActualVelocity) * Convert.ToSingle(kZoomFactor);
                            sizeF = new SizeF(Convert.ToSingle(Probe.ActualVelocity) * Convert.ToSingle(kZoomFactor), Convert.ToSingle(Probe.bp) * Convert.ToSingle(kZoomFactor));
                            scanBlock.column = FaceScanningXBlockNumber - (ScanBlockCount - scanBlock.row * FaceScanningXBlockNumber) - 1;
                        }

                        /*
                        if (scanBlock.row == (FaceScanningYBlockNumber - 1))    //最后一行。。。。。。。。。。
                        {
                            //此处的 REF_POS_Y 为过滤器的一个宽边在画布上的Y坐标值，下面这句代码的意思是当扫描到最后一行时，计算确定另一个宽边在画布上的Y坐标值
                            RealTimeLocationF.Y = Convert.ToSingle(Constant.REF_POS_Y - Filter.FoldingWidth * Convert.ToSingle(kZoomFactor));        //根据过滤器的原点位置和其宽度确定这个数值
                        }
                        */

                        FilterRect = new RectangleF(RealTimePos1, sizeF);
                        BlockPaintBrush = new LinearGradientBrush(FilterRect, Color.Blue, Color.Blue, LinearGradientMode.Horizontal);

                        uint UpstreamOPC_PN_AT_MPPS = 0;
                        uint DownstreamOPC_PN_AT_MPPS = 0;

                        /*
                         * ch1 0.10um--0.15um
                         * ch2 0.15um--0.20um
                         * ch3 0.20um--0.25um
                         * ch4 0.25um--0.30um
                         * ch5 0.30um--0.50um
                         * ch6 0.50um--0.70um
                         * ch7 0.70um--1.00um
                         * ch8 1.00um--INFum
                         */

                        switch (GlobalVariables.strMPPS)
                        {
                            case Constant.MPPS_010_015UM:
                                UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch1;
                                DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC1.PN.ch1;
                                break;
                            case Constant.MPPS_015_020UM:
                                UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch2;
                                DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC1.PN.ch2;
                                break;
                            case Constant.MPPS_020_025UM:
                                UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch3;
                                DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC1.PN.ch3;
                                break;
                            case Constant.MPPS_025_030UM:
                                UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch4;
                                DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC1.PN.ch4;
                                break;
                            case Constant.MPPS_030_050UM:
                                UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch5;
                                DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC1.PN.ch5;
                                break;
                            case Constant.MPPS_050_070UM:
                                UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch6;
                                DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC1.PN.ch6;
                                break;
                            case Constant.MPPS_070_100UM:
                                UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch7;
                                DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC1.PN.ch7;
                                break;
                            case Constant.MPPS_100_INFUM:
                                UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch8;
                                DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC1.PN.ch8;
                                break;
                            default:
                                break;
                        }


                        /**************************考虑了泊松分布后的计算***********************************************/
                        double v, y;
                        v = (DownstreamOPC_PN_AT_MPPS + 1) * 2;
                        y = 0.025;

                        if (v < 1)
                        {
                            v = 1;
                        }

                        scanBlock.RawPN = Convert.ToInt32(chisquaredistr.invchisquaredistribution(v, y) / 2);  //考虑了泊松分布后的单次测量粒子数上限值，95%置信区间

                        //特别注意下面表达式中的v可能会出现非数值的错误，原因是DilutionRation, LocalPenetration, and kb没有赋初值，所以引起被零除。
                        v = (UpstreamOPC_PN_AT_MPPS / (GlobalVariables.DilutionRatio) * Filter.LocalPenetration * GlobalVariables.kb) * 2;
                        y = 0.975;

                        if (v < 1)
                        {
                            v = 1;
                        }
                        scanBlock.ExpectedPN = Convert.ToInt32(chisquaredistr.invchisquaredistribution(v, y) / 2);  //考虑了泊松分布后的单次测量粒子数下限值，95%置信区间

                        /*********************************************************************************/

                        if (scanBlock.RawPN > scanBlock.ExpectedPN)
                        {
                            BlockPaintBrush = new LinearGradientBrush(FilterRect, Color.Red, Color.Red, LinearGradientMode.Horizontal);
                            DataTable.PossibleLeakBlocks.Add(scanBlock);
                        }
                        else
                        {
                            BlockPaintBrush = new LinearGradientBrush(FilterRect, Color.Blue, Color.Blue, LinearGradientMode.Horizontal);
                        }

                        bkGraphics.FillRectangle(BlockPaintBrush, FilterRect);
                        Invalidate();

                        //将上游和下游粒子数数据实时显示在ScanningDataChart上。。。。。。
                        ScanningDataChart.Series["Downstream"].Points[(int)ScanBlockCount].YValues[0] = scanBlock.RawPN;  //等到下面的上游数据加入序列后一起更新
                        ScanningDataChart.Series["Upstream"].Points[(int)ScanBlockCount].YValues[0] = scanBlock.ExpectedPN;
                        ScanningDataChart.Invalidate();

                        //GlobalVariables.log.Error("Row = " + scanBlock.row.ToString() + "\n" + "Column = " + scanBlock.column.ToString() + "\n\n\n");

                        //保存下游扫描数据到数组DownScanningData
                        DownScanningData[scanBlock.row, scanBlock.column] = scanBlock;

                        //保存上游扫描数据到数组UpScanningData
                        scanBlock.RawPN = Convert.ToInt32(UpstreamOPC_PN_AT_MPPS * 1 / (GlobalVariables.DilutionRatio));
                        UpScanningData[scanBlock.row, scanBlock.column] = scanBlock;

                        ScanBlockCount++;

                        DownOPC1DataCount++;

                        DownOPC1DataCountTBox.Text = DownOPC1DataCount.ToString();
                    }

                    if (ScanningRegion == MIDDLE_PARTS)
                    {
                        //MessageBox.Show("In DownOPC1 MIDDLE_PARTS......");

                        GlobalVariables.DownOPC1ResponseEventCount++;

                        ScanDataBlock scanBlock = new ScanDataBlock();

                        sw4ResponseEventDown.Stop();

                        GlobalVariables.strDownOPC1EventInfo += "OPC New Data Ready Event Response @ : " + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";
                        GlobalVariables.strDownOPC1EventInfo += "Time Elapsed: " + sw4ResponseEventDown.ElapsedMilliseconds.ToString() + " million second \n\n";

                        sw4ResponseEventDown.Reset();

                        sw4ResponseEventDown.Start();

                        GlobalVariables.strScanningProcessingInfo += "Get " + DownOPC1DataCount.ToString() + "th OPCData in this Row AT "
                                + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";

                        scanBlock.row = ScanBlockCount / FaceScanningXBlockNumber;

                        if (ScanningMode == FACE_SCANNING_X_DIR123)
                        {
                            RealTimePos1.X += Convert.ToSingle(Probe.ActualVelocity) * Convert.ToSingle(kZoomFactor);
                            sizeF = new SizeF(Convert.ToSingle(Probe.ActualVelocity) * Convert.ToSingle(kZoomFactor), Convert.ToSingle(Probe.bp) * Convert.ToSingle(kZoomFactor));

                            //暂时注释掉
                            //scanBlock.column = ScanBlockCount - scanBlock.row * FaceScanningXBlockNumber;
                        }

                        if (ScanningMode == FACE_SCANNING_X_DIR321)
                        {
                            RealTimePos1.X -= Convert.ToSingle(Probe.ActualVelocity) * Convert.ToSingle(kZoomFactor);
                            sizeF = new SizeF(Convert.ToSingle(Probe.ActualVelocity) * Convert.ToSingle(kZoomFactor), Convert.ToSingle(Probe.bp) * Convert.ToSingle(kZoomFactor));

                            //暂时注释掉
                            //scanBlock.column = FaceScanningXBlockNumber - (ScanBlockCount - scanBlock.row * FaceScanningXBlockNumber) - 1;
                        }

                        /*
                        if (scanBlock.row == (FaceScanningYBlockNumber - 1))    //最后一行。。。。。。。。。。
                        {
                            //此处的 REF_POS_Y 为过滤器的一个宽边在画布上的Y坐标值，下面这句代码的意思是当扫描到最后一行时，计算确定另一个宽边在画布上的Y坐标值
                            RealTimeLocationF.Y = Convert.ToSingle(Constant.REF_POS_Y - Filter.FoldingWidth * Convert.ToSingle(kZoomFactor));        //根据过滤器的原点位置和其宽度确定这个数值
                        }
                        */

                        FilterRect = new RectangleF(RealTimePos1, sizeF);
                        BlockPaintBrush = new LinearGradientBrush(FilterRect, Color.Blue, Color.Blue, LinearGradientMode.Horizontal);

                        uint UpstreamOPC_PN_AT_MPPS = 0;
                        uint DownstreamOPC_PN_AT_MPPS = 0;

                        /*
                         * ch1 0.10um--0.15um
                         * ch2 0.15um--0.20um
                         * ch3 0.20um--0.25um
                         * ch4 0.25um--0.30um
                         * ch5 0.30um--0.50um
                         * ch6 0.50um--0.70um
                         * ch7 0.70um--1.00um
                         * ch8 1.00um--INFum
                         */

                        switch (GlobalVariables.strMPPS)
                        {
                            case Constant.MPPS_010_015UM:
                                UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch1;
                                DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC1.PN.ch1;
                                break;
                            case Constant.MPPS_015_020UM:
                                UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch2;
                                DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC1.PN.ch2;
                                break;
                            case Constant.MPPS_020_025UM:
                                UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch3;
                                DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC1.PN.ch3;
                                break;
                            case Constant.MPPS_025_030UM:
                                UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch4;
                                DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC1.PN.ch4;
                                break;
                            case Constant.MPPS_030_050UM:
                                UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch5;
                                DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC1.PN.ch5;
                                break;
                            case Constant.MPPS_050_070UM:
                                UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch6;
                                DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC1.PN.ch6;
                                break;
                            case Constant.MPPS_070_100UM:
                                UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch7;
                                DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC1.PN.ch7;
                                break;
                            case Constant.MPPS_100_INFUM:
                                UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch8;
                                DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC1.PN.ch8;
                                break;
                            default:
                                break;
                        }


                        /**************************考虑了泊松分布后的计算***********************************************/
                        double v, y;
                        v = (DownstreamOPC_PN_AT_MPPS + 1) * 2;
                        y = 0.025;

                        if (v < 1)
                        {
                            v = 1;
                        }

                        scanBlock.RawPN = Convert.ToInt32(chisquaredistr.invchisquaredistribution(v, y) / 2);  //考虑了泊松分布后的单次测量粒子数上限值，95%置信区间

                        //特别注意下面表达式中的v可能会出现非数值的错误，原因是DilutionRation, LocalPenetration, and kb没有赋初值，所以引起被零除。
                        v = (UpstreamOPC_PN_AT_MPPS / (GlobalVariables.DilutionRatio) * Filter.LocalPenetration * GlobalVariables.kb) * 2;
                        y = 0.975;

                        if (v < 1)
                        {
                            v = 1;
                        }
                        scanBlock.ExpectedPN = Convert.ToInt32(chisquaredistr.invchisquaredistribution(v, y) / 2);  //考虑了泊松分布后的单次测量粒子数下限值，95%置信区间

                        /*********************************************************************************/

                        if (scanBlock.RawPN > scanBlock.ExpectedPN)
                        {
                            BlockPaintBrush = new LinearGradientBrush(FilterRect, Color.Red, Color.Red, LinearGradientMode.Horizontal);
                            DataTable.PossibleLeakBlocks.Add(scanBlock);
                        }
                        else
                        {
                            BlockPaintBrush = new LinearGradientBrush(FilterRect, Color.Blue, Color.Blue, LinearGradientMode.Horizontal);
                        }

                        bkGraphics.FillRectangle(BlockPaintBrush, FilterRect);
                        Invalidate();

                        //将上游和下游粒子数数据实时显示在ScanningDataChart上。。。。。。
                        ScanningDataChart.Series["Downstream"].Points[(int)ScanBlockCount].YValues[0] = scanBlock.RawPN;  //等到下面的上游数据加入序列后一起更新
                        ScanningDataChart.Series["Upstream"].Points[(int)ScanBlockCount].YValues[0] = scanBlock.ExpectedPN;
                        ScanningDataChart.Invalidate();

                        //GlobalVariables.log.Error("Row = " + scanBlock.row.ToString() + "\n" + "Column = " + scanBlock.column.ToString() + "\n\n\n");

                        //为了检查错误，先把下面可能会出问题的代码注释掉
                        /*
                        //保存下游扫描数据到数组DownScanningData
                        DownScanningData[scanBlock.row, scanBlock.column] = scanBlock;

                        //保存上游扫描数据到数组UpScanningData
                        scanBlock.RawPN = Convert.ToInt32(UpstreamOPC_PN_AT_MPPS * 1 / (GlobalVariables.DilutionRatio));
                        UpScanningData[scanBlock.row, scanBlock.column] = scanBlock;
                        */

                        ScanBlockCount++;

                        DownOPC1DataCount++;

                        DownOPC1DataCountTBox.Text = DownOPC1DataCount.ToString();
                    }
                }
                #endregion
            }
            catch (System.Exception ex)
            {
                GlobalVariables.log.Error("Error occured @ function OPCNewDataReadyEventResponse in ScannerForm.cs" + ex.ToString());
            }
        }

        public void DownOPC2NewDataReadyEventResponse2(Remote1104 sender, System.EventArgs e)
        {
            DownOPC2ResponseThread = new Thread(DownOPC2Response2);
            DownOPC2ResponseThread.Start();
        }

        public void DownOPC2Response2()
        {
            try
            {
                //sender.ID == OPC_ID.DOWNSTREAM_OPC1

                //sender.ID == OPC_ID.DOWNSTREAM_OPC2
                #region
                //if (sender.ID == OPC_ID.DOWNSTREAM_OPC2)
                {
                    if (ScanningRegion == LAST_LINE)
                    {

                        //MessageBox.Show("In last line mode......");
                        GlobalVariables.DownOPC1ResponseEventCount++;

                        ScanDataBlock scanBlock = new ScanDataBlock();

                        sw4ResponseEventDown.Stop();

                        GlobalVariables.strDownOPC1EventInfo += "OPC New Data Ready Event Response @ : " + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";
                        GlobalVariables.strDownOPC1EventInfo += "Time Elapsed: " + sw4ResponseEventDown.ElapsedMilliseconds.ToString() + " million second \n\n";

                        sw4ResponseEventDown.Reset();

                        sw4ResponseEventDown.Start();

                        GlobalVariables.strScanningProcessingInfo += "Get " + DownOPC1DataCount.ToString() + "th OPCData in this Row AT "
                                + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";

                        scanBlock.row = ScanBlockCount / FaceScanningXBlockNumber;

                        if (ScanningMode == FACE_SCANNING_X_DIR123)
                        {
                            RealTimePos2.X += Convert.ToSingle(Probe.ActualVelocity) * Convert.ToSingle(kZoomFactor);
                            sizeF = new SizeF(Convert.ToSingle(Probe.ActualVelocity) * Convert.ToSingle(kZoomFactor), Convert.ToSingle(Probe.bp) * Convert.ToSingle(kZoomFactor));
                            scanBlock.column = ScanBlockCount - scanBlock.row * FaceScanningXBlockNumber;
                        }

                        if (ScanningMode == FACE_SCANNING_X_DIR321)
                        {
                            RealTimePos2.X -= Convert.ToSingle(Probe.ActualVelocity) * Convert.ToSingle(kZoomFactor);
                            sizeF = new SizeF(Convert.ToSingle(Probe.ActualVelocity) * Convert.ToSingle(kZoomFactor), Convert.ToSingle(Probe.bp) * Convert.ToSingle(kZoomFactor));
                            scanBlock.column = FaceScanningXBlockNumber - (ScanBlockCount - scanBlock.row * FaceScanningXBlockNumber) - 1;
                        }

                        /*
                        if (scanBlock.row == (FaceScanningYBlockNumber - 1))    //最后一行。。。。。。。。。。
                        {
                            //此处的 REF_POS_Y 为过滤器的一个宽边在画布上的Y坐标值，下面这句代码的意思是当扫描到最后一行时，计算确定另一个宽边在画布上的Y坐标值
                            RealTimeLocationF.Y = Convert.ToSingle(Constant.REF_POS_Y - Filter.FoldingWidth * Convert.ToSingle(kZoomFactor));        //根据过滤器的原点位置和其宽度确定这个数值
                        }
                        */

                        FilterRect = new RectangleF(RealTimePos2, sizeF);
                        BlockPaintBrush = new LinearGradientBrush(FilterRect, Color.Blue, Color.Blue, LinearGradientMode.Horizontal);

                        uint UpstreamOPC_PN_AT_MPPS = 0;
                        uint DownstreamOPC_PN_AT_MPPS = 0;

                        /*
                         * ch1 0.10um--0.15um
                         * ch2 0.15um--0.20um
                         * ch3 0.20um--0.25um
                         * ch4 0.25um--0.30um
                         * ch5 0.30um--0.50um
                         * ch6 0.50um--0.70um
                         * ch7 0.70um--1.00um
                         * ch8 1.00um--INFum
                         */

                        switch (GlobalVariables.strMPPS)
                        {
                            case Constant.MPPS_010_015UM:
                                UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch1;
                                DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC2.PN.ch1;
                                break;
                            case Constant.MPPS_015_020UM:
                                UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch2;
                                DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC2.PN.ch2;
                                break;
                            case Constant.MPPS_020_025UM:
                                UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch3;
                                DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC2.PN.ch3;
                                break;
                            case Constant.MPPS_025_030UM:
                                UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch4;
                                DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC2.PN.ch4;
                                break;
                            case Constant.MPPS_030_050UM:
                                UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch5;
                                DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC2.PN.ch5;
                                break;
                            case Constant.MPPS_050_070UM:
                                UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch6;
                                DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC2.PN.ch6;
                                break;
                            case Constant.MPPS_070_100UM:
                                UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch7;
                                DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC2.PN.ch7;
                                break;
                            case Constant.MPPS_100_INFUM:
                                UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch8;
                                DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC2.PN.ch8;
                                break;
                            default:
                                break;
                        }


                        /**************************考虑了泊松分布后的计算***********************************************/
                        double v, y;
                        v = (DownstreamOPC_PN_AT_MPPS + 1) * 2;
                        y = 0.025;

                        if (v < 1)
                        {
                            v = 1;
                        }

                        scanBlock.RawPN = Convert.ToInt32(chisquaredistr.invchisquaredistribution(v, y) / 2);  //考虑了泊松分布后的单次测量粒子数上限值，95%置信区间

                        //特别注意下面表达式中的v可能会出现非数值的错误，原因是DilutionRation, LocalPenetration, and kb没有赋初值，所以引起被零除。
                        v = (UpstreamOPC_PN_AT_MPPS / (GlobalVariables.DilutionRatio) * Filter.LocalPenetration * GlobalVariables.kb) * 2;
                        y = 0.975;

                        if (v < 1)
                        {
                            v = 1;
                        }
                        scanBlock.ExpectedPN = Convert.ToInt32(chisquaredistr.invchisquaredistribution(v, y) / 2);  //考虑了泊松分布后的单次测量粒子数下限值，95%置信区间

                        /*********************************************************************************/

                        if (scanBlock.RawPN > scanBlock.ExpectedPN)
                        {
                            BlockPaintBrush = new LinearGradientBrush(FilterRect, Color.Red, Color.Red, LinearGradientMode.Horizontal);
                            DataTable.PossibleLeakBlocks.Add(scanBlock);
                        }
                        else
                        {
                            BlockPaintBrush = new LinearGradientBrush(FilterRect, Color.Blue, Color.Blue, LinearGradientMode.Horizontal);
                        }

                        bkGraphics.FillRectangle(BlockPaintBrush, FilterRect);
                        Invalidate();

                        //将上游和下游粒子数数据实时显示在ScanningDataChart上。。。。。。
                        ScanningDataChart.Series["Downstream"].Points[Convert.ToInt32(FaceScanningXBlockNumber * FaceScanningYBlockNumber - DownOPC2DataCount - 1)].YValues[0] = scanBlock.RawPN;  //等到下面的上游数据加入序列后一起更新
                        ScanningDataChart.Series["Upstream"].Points[Convert.ToInt32(FaceScanningXBlockNumber * FaceScanningYBlockNumber - DownOPC2DataCount - 1)].YValues[0] = scanBlock.ExpectedPN;
                        ScanningDataChart.Invalidate();

                        //保存下游扫描数据到数组DownScanningData
                        DownScanningData[FaceScanningYBlockNumber - 1, FaceScanningXBlockNumber - DownOPC2DataCount - 1] = scanBlock;

                        //保存上游扫描数据到数组UpScanningData
                        scanBlock.RawPN = Convert.ToInt32(UpstreamOPC_PN_AT_MPPS * 1 / (GlobalVariables.DilutionRatio));
                        UpScanningData[FaceScanningYBlockNumber - 1, FaceScanningXBlockNumber - DownOPC2DataCount - 1] = scanBlock;

                        ScanBlockCount++;

                        DownOPC2DataCount++;

                        //DownOPC2DataCountTBox.Text = DownOPC2DataCount.ToString();
                    }

                    if (ScanningRegion == MIDDLE_PARTS)
                    {
                        //MessageBox.Show("In DownOPC2 MIDDLE_PARTS......");
                        GlobalVariables.DownOPC1ResponseEventCount++;

                        ScanDataBlock scanBlock = new ScanDataBlock();

                        sw4ResponseEventDown.Stop();

                        GlobalVariables.strDownOPC1EventInfo += "OPC New Data Ready Event Response @ : " + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";
                        GlobalVariables.strDownOPC1EventInfo += "Time Elapsed: " + sw4ResponseEventDown.ElapsedMilliseconds.ToString() + " million second \n\n";

                        sw4ResponseEventDown.Reset();

                        sw4ResponseEventDown.Start();

                        GlobalVariables.strScanningProcessingInfo += "Get " + DownOPC1DataCount.ToString() + "th OPCData in this Row AT "
                                + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";

                        scanBlock.row = ScanBlockCount / FaceScanningXBlockNumber;

                        if (ScanningMode == FACE_SCANNING_X_DIR123)
                        {
                            RealTimePos2.X += Convert.ToSingle(Probe.ActualVelocity) * Convert.ToSingle(kZoomFactor);
                            sizeF = new SizeF(Convert.ToSingle(Probe.ActualVelocity) * Convert.ToSingle(kZoomFactor), Convert.ToSingle(Probe.bp) * Convert.ToSingle(kZoomFactor));
                            scanBlock.column = ScanBlockCount - scanBlock.row * FaceScanningXBlockNumber;
                        }

                        if (ScanningMode == FACE_SCANNING_X_DIR321)
                        {
                            RealTimePos2.X -= Convert.ToSingle(Probe.ActualVelocity) * Convert.ToSingle(kZoomFactor);
                            sizeF = new SizeF(Convert.ToSingle(Probe.ActualVelocity) * Convert.ToSingle(kZoomFactor), Convert.ToSingle(Probe.bp) * Convert.ToSingle(kZoomFactor));
                            scanBlock.column = FaceScanningXBlockNumber - (ScanBlockCount - scanBlock.row * FaceScanningXBlockNumber) - 1;
                        }

                        /*
                        if (scanBlock.row == (FaceScanningYBlockNumber - 1))    //最后一行。。。。。。。。。。
                        {
                            //此处的 REF_POS_Y 为过滤器的一个宽边在画布上的Y坐标值，下面这句代码的意思是当扫描到最后一行时，计算确定另一个宽边在画布上的Y坐标值
                            RealTimeLocationF.Y = Convert.ToSingle(Constant.REF_POS_Y - Filter.FoldingWidth * Convert.ToSingle(kZoomFactor));        //根据过滤器的原点位置和其宽度确定这个数值
                        }
                        */

                        FilterRect = new RectangleF(RealTimePos2, sizeF);
                        BlockPaintBrush = new LinearGradientBrush(FilterRect, Color.Blue, Color.Blue, LinearGradientMode.Horizontal);

                        uint UpstreamOPC_PN_AT_MPPS = 0;
                        uint DownstreamOPC_PN_AT_MPPS = 0;

                        /*
                         * ch1 0.10um--0.15um
                         * ch2 0.15um--0.20um
                         * ch3 0.20um--0.25um
                         * ch4 0.25um--0.30um
                         * ch5 0.30um--0.50um
                         * ch6 0.50um--0.70um
                         * ch7 0.70um--1.00um
                         * ch8 1.00um--INFum
                         */

                        switch (GlobalVariables.strMPPS)
                        {
                            case Constant.MPPS_010_015UM:
                                UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch1;
                                DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC2.PN.ch1;
                                break;
                            case Constant.MPPS_015_020UM:
                                UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch2;
                                DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC2.PN.ch2;
                                break;
                            case Constant.MPPS_020_025UM:
                                UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch3;
                                DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC2.PN.ch3;
                                break;
                            case Constant.MPPS_025_030UM:
                                UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch4;
                                DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC2.PN.ch4;
                                break;
                            case Constant.MPPS_030_050UM:
                                UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch5;
                                DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC2.PN.ch5;
                                break;
                            case Constant.MPPS_050_070UM:
                                UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch6;
                                DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC2.PN.ch6;
                                break;
                            case Constant.MPPS_070_100UM:
                                UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch7;
                                DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC2.PN.ch7;
                                break;
                            case Constant.MPPS_100_INFUM:
                                UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch8;
                                DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC2.PN.ch8;
                                break;
                            default:
                                break;
                        }


                        /**************************考虑了泊松分布后的计算***********************************************/
                        double v, y;
                        v = (DownstreamOPC_PN_AT_MPPS + 1) * 2;
                        y = 0.025;

                        if (v < 1)
                        {
                            v = 1;
                        }

                        scanBlock.RawPN = Convert.ToInt32(chisquaredistr.invchisquaredistribution(v, y) / 2);  //考虑了泊松分布后的单次测量粒子数上限值，95%置信区间

                        //特别注意下面表达式中的v可能会出现非数值的错误，原因是DilutionRation, LocalPenetration, and kb没有赋初值，所以引起被零除。
                        v = (UpstreamOPC_PN_AT_MPPS / (GlobalVariables.DilutionRatio) * Filter.LocalPenetration * GlobalVariables.kb) * 2;
                        y = 0.975;

                        if (v < 1)
                        {
                            v = 1;
                        }
                        scanBlock.ExpectedPN = Convert.ToInt32(chisquaredistr.invchisquaredistribution(v, y) / 2);  //考虑了泊松分布后的单次测量粒子数下限值，95%置信区间

                        /*********************************************************************************/

                        if (scanBlock.RawPN > scanBlock.ExpectedPN)
                        {
                            BlockPaintBrush = new LinearGradientBrush(FilterRect, Color.Red, Color.Red, LinearGradientMode.Horizontal);
                            DataTable.PossibleLeakBlocks.Add(scanBlock);
                        }
                        else
                        {
                            BlockPaintBrush = new LinearGradientBrush(FilterRect, Color.Blue, Color.Blue, LinearGradientMode.Horizontal);
                        }

                        bkGraphics.FillRectangle(BlockPaintBrush, FilterRect);
                        Invalidate();

                        //将上游和下游粒子数数据实时显示在ScanningDataChart上。。。。。。
                        ScanningDataChart.Series["Downstream"].Points[Convert.ToInt32(FaceScanningXBlockNumber * FaceScanningYBlockNumber - DownOPC2DataCount - 1)].YValues[0] = scanBlock.RawPN;  //等到下面的上游数据加入序列后一起更新
                        ScanningDataChart.Series["Upstream"].Points[Convert.ToInt32(FaceScanningXBlockNumber * FaceScanningYBlockNumber - DownOPC2DataCount - 1)].YValues[0] = scanBlock.ExpectedPN;
                        ScanningDataChart.Invalidate();

                        //保存下游扫描数据到数组DownScanningData
                        DownScanningData[FaceScanningYBlockNumber - 1, FaceScanningXBlockNumber - DownOPC2DataCount - 1] = scanBlock;

                        //保存上游扫描数据到数组UpScanningData
                        scanBlock.RawPN = Convert.ToInt32(UpstreamOPC_PN_AT_MPPS * 1 / (GlobalVariables.DilutionRatio));
                        UpScanningData[FaceScanningYBlockNumber - 1, FaceScanningXBlockNumber - DownOPC2DataCount - 1] = scanBlock;

                        ScanBlockCount++;

                        DownOPC2DataCount++;

                        //DownOPC2DataCountTBox.Text = DownOPC2DataCount.ToString();
                    }
                }
                #endregion
            }
            catch (System.Exception ex)
            {
                GlobalVariables.log.Error("Error occured @ function OPCNewDataReadyEventResponse in ScannerForm.cs" + ex.ToString());
            }
        }

        public void UpOPCNewDataReadyEventResponse2(Remote1104 sender, System.EventArgs e)
        {
            UpOPCResponseThread = new Thread(UpOPCResponse2);
            UpOPCResponseThread.Start();
        }

        public void UpOPCResponse2()
        {
            try
            {
                //sender.ID == OPC_ID.DOWNSTREAM_OPC1

                //if (sender.ID == OPC_ID.UPSTREAM_OPC)
                {

                    //GlobalVariables.strUpstreamOPCEventInfo += DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";

                    sw4ResponseEventUp.Stop();

                    //GlobalVariables.strUpstreamOPCEventInfo += "Time Elapsed: " + sw4ResponseEventUp.ElapsedMilliseconds.ToString() + "\n\n\n";

                    GlobalVariables.strUpOPCEventInfo += sw4ResponseEventUp.ElapsedMilliseconds.ToString() + "\n";

                    sw4ResponseEventUp.Reset();
                    sw4ResponseEventUp.Start();

                    uint UpstreamOPC_PN_AT_MPPS = 0;
                    //uint DownstreamOPC_PN_AT_MPPS = 0;

                    /*
                     * ch1 0.10um--0.15um
                     * ch2 0.15um--0.20um
                     * ch3 0.20um--0.25um
                     * ch4 0.25um--0.30um
                     * ch5 0.30um--0.50um
                     * ch6 0.50um--0.70um
                     * ch7 0.70um--1.00um
                     * ch8 1.00um--INFum
                     */

                    switch (GlobalVariables.strMPPS)
                    {
                        case Constant.MPPS_010_015UM:
                            UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch1;
                            //DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC1.PN.ch1;
                            break;
                        case Constant.MPPS_015_020UM:
                            UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch2;
                            //DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC1.PN.ch2;
                            break;
                        case Constant.MPPS_020_025UM:
                            UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch3;
                            //DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC1.PN.ch3;
                            break;
                        case Constant.MPPS_025_030UM:
                            UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch4;
                            //DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC1.PN.ch4;
                            break;
                        case Constant.MPPS_030_050UM:
                            UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch5;
                            //DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC1.PN.ch5;
                            break;
                        case Constant.MPPS_050_070UM:
                            UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch6;
                            //DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC1.PN.ch6;
                            break;
                        case Constant.MPPS_070_100UM:
                            UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch7;
                            //DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC1.PN.ch7;
                            break;
                        case Constant.MPPS_100_INFUM:
                            UpstreamOPC_PN_AT_MPPS = Dev.UpstreamOPC.PN.ch8;
                            //DownstreamOPC_PN_AT_MPPS = Dev.DownstreamOPC1.PN.ch8;
                            break;
                        default:
                            break;
                    }

                    string UpTimestamp = Dev.UpstreamOPC.PN.Timestamp;
                    uint UpCh1 = Dev.UpstreamOPC.PN.ch1;
                    uint UpCh2 = Dev.UpstreamOPC.PN.ch2;
                    uint UpCh3 = Dev.UpstreamOPC.PN.ch3;
                    uint UpCh4 = Dev.UpstreamOPC.PN.ch4;
                    uint UpCh5 = Dev.UpstreamOPC.PN.ch5;
                    uint UpCh6 = Dev.UpstreamOPC.PN.ch6;
                    uint UpCh7 = Dev.UpstreamOPC.PN.ch7;
                    uint UpCh8 = Dev.UpstreamOPC.PN.ch8;

                    //上游的粒子数浓度更新。。。。。。；应该再详细解释下面表达式中的各个数值量。
                    UpConcentrationTBox.Text = (UpstreamOPC_PN_AT_MPPS / (GlobalVariables.DilutionRatio) / (28.3 * 1000 / 60)).ToString("N2");

                    //471.66667计算说明
                    /****************************************************************
                     * 
                     *         28.3*1000 cm^3/(60s) = 471.66667 cm^3/s
                     * 
                     ****************************************************************/
                }
            }
            catch (System.Exception ex)
            {
                GlobalVariables.log.Error("Error occured @ function OPCNewDataReadyEventResponse in ScannerForm.cs" + ex.ToString());
            }
        }

        private void PreparingScanning2()
        {
            GlobalVariables.strTime4FileNameSuffix = DateTime.Now.Year.ToString() + DateTime.Now.Month.ToString() + DateTime.Now.Day.ToString()
                                                    + DateTime.Now.Hour.ToString() + DateTime.Now.Minute.ToString() + DateTime.Now.Second.ToString();

            GlobalVariables.strScanningProcessingInfo = "";
            GlobalVariables.strScanningProcessingInfo += "Preparing Scanning (setting parameters and calculate variables for scanning) \n";

            kZoomFactor = 0.8;
           
            bkBitmap = new Bitmap(Constant.BK_BITMAP_WIDTH, Constant.BK_BITMAP_HEIGHT);

            bkGraphics = Graphics.FromImage((System.Drawing.Image)bkBitmap);

            /******************绘制过滤器的边框**********************************/
            rectangleSize = new Size((int)(Filter.FoldingLength * kZoomFactor), (int)(Filter.FoldingWidth * kZoomFactor));//过滤器边界
            rectangleTopLeft.X = Constant.REF_POS_X;
            rectangleTopLeft.Y = Constant.REF_POS_Y - rectangleSize.Height;

            Rectangle RectangleArea = new Rectangle(rectangleTopLeft, rectangleSize);//过滤器图形示意图
            bkGraphics.DrawRectangle(bluePen, RectangleArea);
            /************************************************************************/

            /***********************计算扫描过程所需要的参数************************/
            FaceScanningXBlockNumber = Convert.ToUInt32(Math.Ceiling(Filter.FoldingLength / Probe.SettingVelocity));    //扫描的列数，对应扫描矩阵的列
            Probe.ActualVelocity = Filter.FoldingLength / FaceScanningXBlockNumber;         //此为实际的扫描速度，单位为 mm/s
            FaceScanningYBlockNumber = Convert.ToUInt32(Math.Ceiling(Filter.FoldingWidth / Probe.bp));     //扫描的行数，对应扫描矩阵的行
            /************************************************************************/

            /*************************初始化当前扫描块在内存画布上的位置*****************/
            RealTimeLocationF.X = Constant.REF_POS_X;
            RealTimeLocationF.Y = Constant.REF_POS_Y;

            RealTimeLocationF.X -= Convert.ToSingle(Probe.ActualVelocity) * Convert.ToSingle(kZoomFactor);     //协调扫描Block的坐标。。。。。。
            RealTimeLocationF.Y -= Convert.ToSingle(Probe.bp) * Convert.ToSingle(kZoomFactor);
            /**************************************************************************/

            /******************************初始化扫描数据矩阵*************************************/
            DownScanningData = new ScanDataBlock[FaceScanningYBlockNumber, FaceScanningXBlockNumber];
            UpScanningData = new ScanDataBlock[FaceScanningYBlockNumber, FaceScanningXBlockNumber];
            /**************************************************************************************/

            //清空之前的可能的Leak Blocks!
            DataTable.PossibleLeakBlocks.Clear();

            /**************************ScanBlockCount计数******************************************/
            ScanBlockCount = 0;

            /*************************调试信息******************************/
            GlobalVariables.DownOPC1RunningCount = 0;
            GlobalVariables.DownOPC1ResponseEventCount = 0;

            GlobalVariables.strMotorRunningInfo = "";
            Dev.DownstreamOPC1.strOPCRunningInfo = "";

            GlobalVariables.strDownOPC1EventInfo = "";
            GlobalVariables.strDownOPC2EventInfo = "";

            GlobalVariables.strUpOPCEventInfo = "";
            /***************************************************************/


            ScanningMode = FACE_SCANNING_X_DIR123;

            
            /*
            //X_DIR123和X_DIR321的选择主要是根据当前扫描探头所在的位置
            if (ScanningModeCBox.SelectedItem.ToString() == "X_DIR123")
            {
                ScanningMode = FACE_SCANNING_X_DIR123;
            }
            else if (ScanningModeCBox.SelectedItem.ToString() == "X_DIR321")
            {
                ScanningMode = FACE_SCANNING_X_DIR321;
            }
             */

            ScanningDataChart.Series["Upstream"].Points.Clear();
            ScanningDataChart.Series["Downstream"].Points.Clear();

            ScanningDataChart.ChartAreas[0].AxisY.Maximum = 5000;

            for (int row = 0; row < FaceScanningYBlockNumber; row++)
            {
                for (int col = 0; col < FaceScanningXBlockNumber; col++)
                {
                    ScanningDataChart.Series["Upstream"].Points.AddY(100.0);
                    ScanningDataChart.Series["Downstream"].Points.AddY(0.0);
                }
            }

            ScanningDataChart.Series["Upstream"].Color = Color.Red;
            ScanningDataChart.Series["Downstream"].Color = Color.Blue;

            ScanningDataChart.Invalidate();     //强制Chart图像重绘当前所有数据点

            Invalidate();

            //扫描过程中的信息......
            GlobalVariables.strScanningProcessingInfo += "\n\n\n";
            GlobalVariables.strScanningProcessingInfo += "Filter Dimensions \n";
            GlobalVariables.strScanningProcessingInfo += "Filter Length: " + Filter.FoldingLength.ToString() + "\n";
            GlobalVariables.strScanningProcessingInfo += "Filter Width: " + Filter.FoldingWidth.ToString() + "\n";
            GlobalVariables.strScanningProcessingInfo += "\n\n\n";

            GlobalVariables.strScanningProcessingInfo += "Scanning Row & Column\n";
            GlobalVariables.strScanningProcessingInfo += "Scanning Row: " + FaceScanningYBlockNumber.ToString() + "\n";
            GlobalVariables.strScanningProcessingInfo += "Scanning Column: " + FaceScanningXBlockNumber.ToString() + "\n";

            GlobalVariables.strScanningProcessingInfo += "\n\n\n";
        }

        private void Scanning2()
        {

            try
            {
                GlobalVariables.strScanningProcessingInfo += "Scanner Initialize......\n";
                Scanner.Initialize();

                GlobalVariables.strScanningProcessingInfo += "Preparing OPCs of Upstream and Downstream......\n";

                //上游OPC开启后会一直开着，直到整个扫描过程结束
                GlobalVariables.strScanningProcessingInfo += "Start Upstream OPC......\n";
                Dev.UpstreamOPC.SimpleConfigure();
                Thread.Sleep(1000);
                Dev.UpstreamOPC.InstrumentStart();

                Dev.DownstreamOPC1.SimpleConfigure();
                Thread.Sleep(1000);
                Dev.DownstreamOPC2.SimpleConfigure();
                Thread.Sleep(1000);

                GlobalVariables.strMotorRunningInfo = "";

                ScanBlockCount = 0;

                int i;

                GlobalVariables.strScanningProcessingInfo += "Starting Scanning......\n";

                sw4Scanning.Start();

                GlobalVariables.strScanningProcessingInfo += "Scanner Start AT : " + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";

                //此处赋值只是为了初始化，在接下来的if语句里才是真正的赋值......
                byte dir = Constant.DIR123;

                if (ScanningMode == FACE_SCANNING_X_DIR123)
                {
                    dir = Constant.DIR123;

                    GlobalVariables.strScanningProcessingInfo += "起始扫描模式为FORWARD，即DIR123...\n";
                }

                if (ScanningMode == FACE_SCANNING_X_DIR321)
                {
                    dir = Constant.DIR321;

                    RealTimeLocationF.X = Constant.REF_POS_X + (float)(Filter.FoldingLength * kZoomFactor);
                    RealTimeLocationF.Y = Constant.REF_POS_Y;
                    RealTimeLocationF.Y -= Convert.ToSingle(Probe.bp) * Convert.ToSingle(kZoomFactor);

                    GlobalVariables.strScanningProcessingInfo += "起始扫描模式为BACKWARD，即DIR321...\n";
                }

                //此种情况在两个扫描探头的情况下不做处理，所以里面的代码不去做任何的修改了。。。。。。               
                if (FaceScanningYBlockNumber == 1)
                {
                    #region
                    GlobalVariables.strScanningProcessingInfo += "处理整个扫描过程只有1行的情况......\n";    //尽管这种情况极少发生，但是还得有相应的处理代码

                    i = 0;

                    DownOPC1DataCount = 0;   //用于同步粒子计数器的计数次数和伺服电机运行的步调

                    GlobalVariables.strScanningProcessingInfo += "启动下游粒子计数器......\n";
                    Dev.DownstreamOPC1.PredefinedSampleCount = Convert.ToInt32(FaceScanningXBlockNumber);
                    Dev.DownstreamOPC1.InstrumentStart();

                    /*****************************************************************************************************
                     为了记录实际的OPC数据采集发生的间隔，此句主要用来检查错误用。这句是每行开始的一个触发，接下来的记录和触发在                   
                     事件响应函数OPCNewDataReadyEventResponse中，最后的停止在一行扫描完成之后还有一句sw4ResponseEventDown.Stop();
                     ******************************************************************************************************/
                    sw4ResponseEventDown.Start();

                    /********************下面这部分代码是扫描一行，主要语句为Scanneer.MoveX(......)，其它是一些调试用信息语句************/
                    GlobalVariables.strMotorRunningInfo += "X_Begin: " + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";

                    sw4MotorRunning.Reset();
                    sw4MotorRunning.Start();

                    GlobalVariables.strScanningProcessingInfo += "Starting " + (2 * i + 1).ToString() + "th ROW scanning AT "
                         + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";

                    Scanner.MoveX(XScanningLength, Probe.ActualVelocity, dir);//应该是步进，滤芯X方向长度-探头ap,2012.04.21,又改回去了.改动太大暂时放弃了，设置下//应该是步进，滤芯X方向长度-探头ap,2012.04.21

                    GlobalVariables.strScanningProcessingInfo += "Stop " + (2 * i + 1).ToString() + "th ROW scanning AT "
                         + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";

                    GlobalVariables.strScanningProcessingInfo += "\n\n\n";

                    sw4MotorRunning.Stop();

                    GlobalVariables.strMotorRunningInfo += "X_End : " + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";
                    GlobalVariables.strMotorRunningInfo += "Time Elapsed: " + sw4MotorRunning.ElapsedMilliseconds.ToString() + " ms\n";
                    GlobalVariables.strMotorRunningInfo += (2 * i + 1).ToString() + "\n";
                    GlobalVariables.strMotorRunningInfo += "\n\n\n";
                    /*****************************************************************************************************************/

                    //此while循环是是同步X方向行扫描和粒子计数器数据采集的，保证粒子计数器采集到足够的次数
                    while (DownOPC1DataCount != FaceScanningXBlockNumber)
                    {
                        GlobalVariables.strScanningProcessingInfo += "Waiting for ROW scanning finish...... \n ";
                        Thread.Sleep(100);
                    }

                    //X方向走完了，FaceScanningXBlockNumber也记录好了，所以要停掉秒表了。 为了记录实际的OPC数据采集发生的间隔
                    sw4ResponseEventDown.Stop();

                    Thread.Sleep(100);
                    #endregion
                }


                //
                //扫描起始边界行，扫描这一行时，只启动靠近过滤器围栏边缘的OPC，绘图时也只是绘一行
                //
                #region

                //表示当前扫描区域为FIRST_LINE
                ScanningRegion = FIRST_LINE;

                //调整绘图坐标，以便于扫描过程中绘图
                RealTimePos1.X = Constant.REF_POS_X;
                RealTimePos1.Y = Constant.REF_POS_Y;
                RealTimePos1.X -= Convert.ToSingle(Probe.ActualVelocity) * Convert.ToSingle(kZoomFactor);     //协调扫描Block的坐标。。。。。。
                RealTimePos1.Y -= Convert.ToSingle(Probe.bp) * Convert.ToSingle(kZoomFactor);         


                DownOPC1DataCount = 0;   //用于同步粒子计数器的计数次数和伺服电机运行的步调

                GlobalVariables.strScanningProcessingInfo += "启动下游粒子计数器......\n";
                Dev.DownstreamOPC1.PredefinedSampleCount = Convert.ToInt32(FaceScanningXBlockNumber);
                Dev.DownstreamOPC1.InstrumentStart();

                /*****************************************************************************************************
                 为了记录实际的OPC数据采集发生的间隔，此句主要用来检查错误用。这句是每行开始的一个触发，接下来的记录和触发在                   
                 事件响应函数OPCNewDataReadyEventResponse中，最后的停止在一行扫描完成之后还有一句sw4ResponseEventDown.Stop();
                 ******************************************************************************************************/
                sw4ResponseEventDown.Start();

                /********************下面这部分代码是扫描一行，主要语句为Scanneer.MoveX(......)，其它是一些调试用信息语句************/
                GlobalVariables.strMotorRunningInfo += "X_Begin: " + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";

                sw4MotorRunning.Reset();
                sw4MotorRunning.Start();

                GlobalVariables.strScanningProcessingInfo += "Starting " + "1" + "th ROW scanning AT "
                     + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";

                Scanner.MoveX(XScanningLength, Probe.ActualVelocity, dir);//应该是步进，滤芯X方向长度-探头ap,2012.04.21,又改回去了.改动太大暂时放弃了，设置下//应该是步进，滤芯X方向长度-探头ap,2012.04.21       //扫描一行............

                GlobalVariables.strScanningProcessingInfo += "Stop " + "1" + "th ROW scanning AT "
                     + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";

                GlobalVariables.strScanningProcessingInfo += "\n\n\n";

                sw4MotorRunning.Stop();

                GlobalVariables.strMotorRunningInfo += "X_End : " + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";
                GlobalVariables.strMotorRunningInfo += "Time Elapsed: " + sw4MotorRunning.ElapsedMilliseconds.ToString() + " ms\n";
                GlobalVariables.strMotorRunningInfo += (1).ToString() + "\n";
                GlobalVariables.strMotorRunningInfo += "\n\n\n";
                /****************************************************************************************************************/

                //此while循环是是同步X方向行扫描和粒子计数器数据采集的，保证粒子计数器采集到足够的次数
                while (DownOPC1DataCount != FaceScanningXBlockNumber)
                {
                    GlobalVariables.strScanningProcessingInfo += "Waiting for ROW scanning finish...... \n ";
                    Thread.Sleep(100);
                }

                //X方向走完了，FaceScanningXBlockNumber也记录好了，所以要停掉秒表了。 为了记录实际的OPC数据采集发生的间隔
                sw4ResponseEventDown.Stop();

                Thread.Sleep(100);
                #endregion


                //////////////////////////////////////////////////////////////////////////////////////////////
                //Y向步进，将探头移动到最后一行位置，准备扫描最后一行，NOTE：这里的2*Probe.bp还需要再仔细考虑考虑，看看是不是正确
                //////////////////////////////////////////////////////////////////////////////////////////////
                Scanner.MoveY(Filter.FoldingWidth - 2*Probe.bp, Probe.ActualVelocity, Constant.DIR321);

                //
                //扫描结束边界行，扫描这一行时，启动靠近过滤器围栏边缘的OPC (另外一个OPC)，绘图时也只是绘一行
                //
                #region

                //表示当前扫描区域为LAST_LINE
                ScanningRegion = LAST_LINE;

                //调整绘图坐标，以便于扫描过程中绘图
                RealTimePos2.X = Convert.ToSingle(Constant.REF_POS_X + Filter.FoldingLength*kZoomFactor);
                RealTimePos2.Y = Convert.ToSingle(Constant.REF_POS_Y - Filter.FoldingWidth*kZoomFactor);

                //这个地方不需要再修改，否则的话效果就不对了
                //RealTimePos2.X -= Convert.ToSingle(Probe.ActualVelocity) * Convert.ToSingle(kZoomFactor);     //协调扫描Block的坐标。。。。。。
                //RealTimePos2.Y -= Convert.ToSingle(Probe.bp) * Convert.ToSingle(kZoomFactor);   

                DownOPC2DataCount = 0;   //用于同步粒子计数器的计数次数和伺服电机运行的步调

                GlobalVariables.strScanningProcessingInfo += "启动下游粒子计数器......\n";
                Dev.DownstreamOPC2.PredefinedSampleCount = Convert.ToInt32(FaceScanningXBlockNumber);
                Dev.DownstreamOPC2.InstrumentStart();

                /*****************************************************************************************************
                 为了记录实际的OPC数据采集发生的间隔，此句主要用来检查错误用。这句是每行开始的一个触发，接下来的记录和触发在                   
                 事件响应函数OPCNewDataReadyEventResponse中，最后的停止在一行扫描完成之后还有一句sw4ResponseEventDown.Stop();
                 ******************************************************************************************************/
                sw4ResponseEventDown.Start();

                /********************下面这部分代码是扫描一行，主要语句为Scanneer.MoveX(......)，其它是一些调试用信息语句************/
                GlobalVariables.strMotorRunningInfo += "X_Begin: " + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";

                sw4MotorRunning.Reset();
                sw4MotorRunning.Start();

                GlobalVariables.strScanningProcessingInfo += "Starting " + "last" + "th ROW scanning AT "
                     + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";

                //确定接下来的扫描方向。。。。。。
                if (ScanningMode == FACE_SCANNING_X_DIR123)
                {
                    dir = Constant.DIR321;
                    ScanningMode = FACE_SCANNING_X_DIR321;
                }
                else if (ScanningMode == FACE_SCANNING_X_DIR321)
                {
                    dir = Constant.DIR123;
                    ScanningMode = FACE_SCANNING_X_DIR123;
                }

                Scanner.MoveX(XScanningLength, Probe.ActualVelocity, dir);//应该是步进，滤芯X方向长度-探头ap,2012.04.21,又改回去了.改动太大暂时放弃了，设置下//应该是步进，滤芯X方向长度-探头ap,2012.04.21       //扫描一行............

                GlobalVariables.strScanningProcessingInfo += "Stop " + "last" + "th ROW scanning AT "
                     + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";

                GlobalVariables.strScanningProcessingInfo += "\n\n\n";

                sw4MotorRunning.Stop();

                GlobalVariables.strMotorRunningInfo += "X_End : " + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";
                GlobalVariables.strMotorRunningInfo += "Time Elapsed: " + sw4MotorRunning.ElapsedMilliseconds.ToString() + " ms\n";
                GlobalVariables.strMotorRunningInfo += (1).ToString() + "\n";
                GlobalVariables.strMotorRunningInfo += "\n\n\n";
                /****************************************************************************************************************/

                //MessageBox.Show("Before while (DownOPC2DataCount != FaceScanningXBlockNumber)");
                //此while循环是是同步X方向行扫描和粒子计数器数据采集的，保证粒子计数器采集到足够的次数
                while (DownOPC2DataCount != FaceScanningXBlockNumber)
                {
                    GlobalVariables.strScanningProcessingInfo += "Waiting for ROW scanning finish...... \n ";
                    Thread.Sleep(100);
                }
                //MessageBox.Show("After while (DownOPC2DataCount != FaceScanningXBlockNumber)");

                //MessageBox.Show("After FIRST_LINE & LAST_LINE......");

                //X方向走完了，FaceScanningXBlockNumber也记录好了，所以要停掉秒表了。 为了记录实际的OPC数据采集发生的间隔
                sw4ResponseEventDown.Stop();

                Thread.Sleep(100);
                #endregion


                /////////////////////////////////////////////////////////////////////////////
                //Y向步进，将探头移动到第一行之后的那行，   NOTE：这里的3*Probe.bp还需要再仔细考虑考虑，看看是不是正确
                ////////////////////////////////////////////////////////////////////////////
                Scanner.MoveY(Filter.FoldingWidth - 3 * Probe.bp, Probe.ActualVelocity, Constant.DIR123);


                //扫描完两个边界行后，接下来的扫描中间的部分，重新计算要扫描的行数
                //是否考虑定义一个新的变量 RestFaceScanningYBlockNumber ......
                uint RestFaceScanningYBlockNumber = FaceScanningYBlockNumber - 2;


                //表示当前扫描区域为FIRST_LINE
                ScanningRegion = MIDDLE_PARTS;

                //调整绘图坐标，以便于扫描过程中绘图
                RealTimePos1.X = Constant.REF_POS_X;
                RealTimePos1.Y = Constant.REF_POS_Y;
                RealTimePos1.X -= Convert.ToSingle(Probe.ActualVelocity) * Convert.ToSingle(kZoomFactor);     //协调扫描Block的坐标。。。。。。
                RealTimePos1.Y -= Convert.ToSingle(2 * Probe.bp) * Convert.ToSingle(kZoomFactor);

                RealTimePos2.X = Constant.REF_POS_X;
                RealTimePos2.Y = Constant.REF_POS_Y;
                RealTimePos2.X -= Convert.ToSingle(Probe.ActualVelocity) * Convert.ToSingle(kZoomFactor);     //协调扫描Block的坐标。。。。。。
                RealTimePos2.Y -= Convert.ToSingle(4 * Probe.bp) * Convert.ToSingle(kZoomFactor);

                //确定接下来的扫描方向。。。。。。
                if (ScanningMode == FACE_SCANNING_X_DIR123)
                {
                    dir = Constant.DIR321;
                    ScanningMode = FACE_SCANNING_X_DIR321;
                }
                else if (ScanningMode == FACE_SCANNING_X_DIR321)
                {
                    dir = Constant.DIR123;
                    ScanningMode = FACE_SCANNING_X_DIR123;
                }

                for (i = 0; i < (FaceScanningYBlockNumber + 1) / 2; i++)//for (i = 0; i < (RestFaceScanningYBlockNumber + 1) / 2; i++)2012.04.20去掉了Rest，因为Y方向总是步进的比设置的少。
                {
                    //MessageBox.Show("In the for-loop......");

                    DownOPC1DataCount = 0;   //用于同步粒子计数器的计数次数和伺服电机运行的步调
                    DownOPC2DataCount = 0;

                    DownOPC1DataCountTBox.Text = DownOPC1DataCount.ToString();
                    //DownOPC2DataCountTBox.Text = DownOPC2DataCount.ToString();

                    GlobalVariables.strScanningProcessingInfo += "启动下游粒子计数器......\n";

                    Dev.DownstreamOPC1.PredefinedSampleCount = Convert.ToInt32(FaceScanningXBlockNumber);
                    Dev.DownstreamOPC1.InstrumentStart();

                    Dev.DownstreamOPC2.PredefinedSampleCount = Convert.ToInt32(FaceScanningXBlockNumber);
                    Dev.DownstreamOPC2.InstrumentStart();

                    /*****************************************************************************************************
                     为了记录实际的OPC数据采集发生的间隔，此句主要用来检查错误用。这句是每行开始的一个触发，接下来的记录和触发在                   
                     事件响应函数OPCNewDataReadyEventResponse中，最后的停止在一行扫描完成之后还有一句sw4ResponseEventDown.Stop();
                     ******************************************************************************************************/
                    sw4ResponseEventDown.Start();

                    /********************下面这部分代码是扫描一行，主要语句为Scanneer.MoveX(......)，其它是一些调试用信息语句************/
                    GlobalVariables.strMotorRunningInfo += "X_Begin: " + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";

                    sw4MotorRunning.Reset();
                    sw4MotorRunning.Start();

                    //这里的变量i是有问题的，需要特别注意，现在先不管它，先管思路的问题。。。。。。。。。
                    GlobalVariables.strScanningProcessingInfo += "Starting " + (2 * i + 1).ToString() + "th ROW scanning AT "
                         + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";

                    //时刻注意方向变量............
                    Scanner.MoveX(XScanningLength, Probe.ActualVelocity, dir);//应该是步进，滤芯X方向长度-探头ap,2012.04.21,又改回去了.改动太大暂时放弃了，设置下//应该是步进，滤芯X方向长度-探头ap,2012.04.21       //扫描一行............

                    GlobalVariables.strScanningProcessingInfo += "Stop " + (2 * i + 1).ToString() + "th ROW scanning AT "
                         + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";

                    GlobalVariables.strScanningProcessingInfo += "\n\n\n";

                    sw4MotorRunning.Stop();

                    GlobalVariables.strMotorRunningInfo += "X_End : " + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";
                    GlobalVariables.strMotorRunningInfo += "Time Elapsed: " + sw4MotorRunning.ElapsedMilliseconds.ToString() + " ms\n";
                    GlobalVariables.strMotorRunningInfo += (2 * i + 1).ToString() + "\n";
                    GlobalVariables.strMotorRunningInfo += "\n\n\n";
                    /****************************************************************************************************************/


                    //MessageBox.Show("Before while ((DownOPC1DataCount != FaceScanningXBlockNumber) || (DownOPC2DataCount != FaceScanningXBlockNumber))......");
                    //此while循环是是同步X方向行扫描和粒子计数器数据采集的，保证粒子计数器采集到足够的次数
                    //while ((DownOPC1DataCount != FaceScanningXBlockNumber) || (DownOPC2DataCount != FaceScanningXBlockNumber))
                    
                    while (DownOPC1DataCount != FaceScanningXBlockNumber)
                    {
                        GlobalVariables.strScanningProcessingInfo += "Waiting for ROW scanning finish...... \n ";
                        Thread.Sleep(100);
                    }

                    //MessageBox.Show("Between two while-loop......");

                    while (DownOPC2DataCount != FaceScanningXBlockNumber)
                    {
                        GlobalVariables.strScanningProcessingInfo += "Waiting for ROW scanning finish...... \n ";
                        Thread.Sleep(100);
                    }


                    //MessageBox.Show("After while ((DownOPC1DataCount != FaceScanningXBlockNumber) || (DownOPC2DataCount != FaceScanningXBlockNumber))......");

                    //X方向走完了，FaceScanningXBlockNumber也记录好了，所以要停掉秒表了。 为了记录实际的OPC数据采集发生的间隔
                    sw4ResponseEventDown.Stop();

                    Thread.Sleep(100);

                    //Y向步进，因为是第一次，直接按照ProbeSize的大小步进是没有问题的，不知道Constant.DIR321方向对不对？等等再管。
                    Scanner.MoveY(Probe.bp, Probe.ActualVelocity, Constant.DIR321);

                    //Y向步进后协调Block块的坐标，这是为了绘图做准备的。。。。。。。。。。。。。
                    RealTimePos1.Y -= Convert.ToSingle(Probe.bp * kZoomFactor);
                    RealTimePos2.Y -= Convert.ToSingle(Probe.bp * kZoomFactor);

                    if (ScanningMode == FACE_SCANNING_X_DIR123)
                    {
                        RealTimePos1.X += Convert.ToSingle(Probe.ActualVelocity * kZoomFactor);
                        RealTimePos2.X += Convert.ToSingle(Probe.ActualVelocity * kZoomFactor);
                    }
                    else if (ScanningMode == FACE_SCANNING_X_DIR321)
                    {
                        RealTimePos1.X -= Convert.ToSingle(Probe.ActualVelocity * kZoomFactor);
                        RealTimePos2.X -= Convert.ToSingle(Probe.ActualVelocity * kZoomFactor);
                    }

                    if (ScanningMode == FACE_SCANNING_X_DIR123)
                    {
                        dir = Constant.DIR321;
                        ScanningMode = FACE_SCANNING_X_DIR321;
                    }
                    else if (ScanningMode == FACE_SCANNING_X_DIR321)
                    {
                        dir = Constant.DIR123;
                        ScanningMode = FACE_SCANNING_X_DIR123;
                    }
                }

                sw4Scanning.Stop();

                /***************************扫描过程中的所有信息记录，最后都保存在文件中，只有扫描完成了才能得到这些文件****************************************/
                GlobalVariables.strScanningProcessingInfo += "Scanner Stop AT: " + DateTime.Now.Hour.ToString() + ":" + DateTime.Now.Minute.ToString() + ":" + DateTime.Now.Second.ToString() + "." + DateTime.Now.Millisecond.ToString() + "\n";
                GlobalVariables.strScanningProcessingInfo += "Finish Scanning......\n";
                GlobalVariables.strScanningProcessingInfo += "\n\n\n";

                GlobalVariables.strScanningProcessingInfo += "Total Scanning Time: " + sw4Scanning.ElapsedMilliseconds / 1000 + " second \n";
                GlobalVariables.strScanningProcessingInfo += "Scanning Velocity: " + Probe.ActualVelocity + " mm/s\n";

                GlobalVariables.strScanningProcessingInfo += "Total OPC Data Count: " + GlobalVariables.DownOPC1ResponseEventCount.ToString() + "\n";

                GlobalVariables.strDownOPC1EventInfo = "Down OPC1 Event Response Total Times: " + GlobalVariables.DownOPC1ResponseEventCount.ToString() + "\n\n\n" + GlobalVariables.strDownOPC1EventInfo;

                Dev.DownstreamOPC1.strOPCRunningInfo = "Down OPC1 Running Times: " + GlobalVariables.DownOPC1RunningCount.ToString() + "\n\n\n" + Dev.DownstreamOPC1.strOPCRunningInfo;

                File.WriteAllText("MotorRunningInfo" + GlobalVariables.strTime4FileNameSuffix + ".txt", GlobalVariables.strMotorRunningInfo);
                File.WriteAllText("DownOPC1RunningInfo" + GlobalVariables.strTime4FileNameSuffix + ".txt", Dev.DownstreamOPC1.strOPCRunningInfo);
                File.WriteAllText("DownOPC1ResponseEvent" + GlobalVariables.strTime4FileNameSuffix + ".txt", GlobalVariables.strDownOPC1EventInfo);
                File.WriteAllText("UpOPCResponseEvent" + GlobalVariables.strTime4FileNameSuffix + ".txt", GlobalVariables.strUpOPCEventInfo);
                File.WriteAllText("ScanningProcessingInfo" + GlobalVariables.strTime4FileNameSuffix + ".txt", GlobalVariables.strScanningProcessingInfo);    //扫描过程中的所有信息都保存在这个文件中......             
                /******************************************************************************************************************************************/

                //此处曾引起过IO异常，有时间的话好好看看。。。。。。。。。这个地方有错会导致它后面的程序代码不能正常执行，所以把它放在了最后面。
                Dev.UpstreamOPC.InstrumentStop();

                //PickupPossibleLeakBlocks();
            }

            catch (System.Exception ex)
            {
                GlobalVariables.log.Error("Error ocured @ function Scanning() in ScannerForm.cs" + ex.ToString());
            }
        }

        #endregion

        #region

        private void MoveToBtn_Click(object sender, EventArgs e)
        {
            Dev.MDriverX.AccelerationSetting();
            Dev.MDriverX.DecelerationSetting();

            Dev.MDriverY.AccelerationSetting();
            Dev.MDriverY.DecelerationSetting();

            Scanner.MoveTo(Convert.ToDouble(X_TBox.Text), Convert.ToDouble(Y_TBox.Text));
        }

        private void RightMoveBtn_Click(object sender, EventArgs e)
        {
            Scanner.AccDecSetting();
            Scanner.MoveX(Convert.ToDouble(ProbeAdjStepLengthTBox.Text), 50, Constant.FOREWARD);
        }

        private void LeftMoveBtn_Click(object sender, EventArgs e)
        {
            Scanner.AccDecSetting();
            Scanner.MoveX(Convert.ToDouble(ProbeAdjStepLengthTBox.Text), 50, Constant.BACKWARD);
        }

        private void UpMoveBtn_Click(object sender, EventArgs e)
        {
            Scanner.AccDecSetting();
            Scanner.MoveY(Convert.ToDouble(ProbeAdjStepLengthTBox.Text), 50, Constant.BACKWARD);
        }

        private void DownMoveBtn_Click(object sender, EventArgs e)
        {
            Scanner.AccDecSetting();
            Scanner.MoveY(Convert.ToDouble(ProbeAdjStepLengthTBox.Text), 50, Constant.FOREWARD);
        }

        private void FrameScanBtn_Click(object sender, EventArgs e)
        {
            try
            {
                //ScanningDataChart.ChartAreas["ChartAreas1"].AxisX.MajorGrid.Interval = FaceScanningXBlockNumber;
                if (GlobalVariables.RunningMode == RUNNING_MODE.REAL)
                {
                    bool IsXServoON, IsYServoON;

                    IsXServoON = Dev.MDriverX.IsServoON;
                    Thread.Sleep(50);

                    IsYServoON = Dev.MDriverY.IsServoON;
                    Thread.Sleep(50);

                    if ((!IsXServoON) || (!IsYServoON))
                    {
                        IsXServoON = Dev.MDriverX.IsServoON;
                        Thread.Sleep(50);

                        IsYServoON = Dev.MDriverY.IsServoON;
                        Thread.Sleep(50);

                        if ((!IsXServoON) || (!IsYServoON))
                        {
                            IsXServoON = Dev.MDriverX.IsServoON;
                            Thread.Sleep(50);

                            IsYServoON = Dev.MDriverY.IsServoON;
                            Thread.Sleep(50);

                            if ((!IsXServoON) || (!IsYServoON))
                            {
                                MessageBox.Show("请首先使能两个伺服电机，然后再按过滤器扫描按钮！");
                                return;
                            }
                        }
                    }
                }
                else if (GlobalVariables.RunningMode == RUNNING_MODE.SIMULATION)
                {
                    //Do Nothing......
                }

                if (Scanner.CurrentX != 0 || Scanner.CurrentY != 0)//double类型与0比较是不是会不太保险？
                {
                    MessageBox.Show("目前扫描头不在原点，请先回原点，然后再进行扫描！");
                    return;
                }

                if (GlobalVariables.ScanningConfiguration == Constant.ONE_PLUS_ONE)
                {
                    PreparingScanning1();
                    ScanningThread1 = new Thread(Scanning1);
                    ScanningThread1.Start();
                }

                if (GlobalVariables.ScanningConfiguration == Constant.ONE_PLUS_TWO)
                {
                    PreparingScanning2();
                    ScanningThread2 = new Thread(Scanning2);
                    ScanningThread2.Start();
                }

            }
            catch (System.Exception ex)
            {
                GlobalVariables.log.Error("Error ocured @ function FrameScanBtn_Click in ScannerForm.cs" + ex.ToString());
            }
        }
        #endregion

        protected override void OnClosed(EventArgs e)
        {
            if (GlobalVariables.ScanningConfiguration == Constant.ONE_PLUS_ONE)
            {
                Dev.DownstreamOPC1.OPCNewDataReadyEvent -= OPCNewDataReadyEventResponse1;

                Dev.UpstreamOPC.OPCNewDataReadyEvent -= OPCNewDataReadyEventResponse1;
            }

            if (GlobalVariables.ScanningConfiguration == Constant.ONE_PLUS_TWO)
            {
                Dev.DownstreamOPC1.OPCNewDataReadyEvent -= DownOPC1NewDataReadyEventResponse2;
                Dev.DownstreamOPC2.OPCNewDataReadyEvent -= DownOPC2NewDataReadyEventResponse2;

                Dev.UpstreamOPC.OPCNewDataReadyEvent -= UpOPCNewDataReadyEventResponse2;
            }
         
            base.OnClosed(e);
        }      

        private void ScanningPreparingBtn_Click(object sender, EventArgs e)
        {
            AuxScanningForm auxScanningForm = new AuxScanningForm();         
            auxScanningForm.ShowDialog();
        }

        private void UpConcentrationMonitorBtn_Click(object sender, EventArgs e)
        {
            Dev.UpstreamOPC.SimpleConfigure();
            Dev.UpstreamOPC.InstrumentStart();
            Dev.UpstreamOPC.PredefinedSampleCount = -1;
        }

        private void StopMonitorBtn_Click(object sender, EventArgs e)
        {
            Dev.UpstreamOPC.InstrumentStop();
        }

        private void Remote1104Btn_Click(object sender, EventArgs e)
        {
            Remote1104Form remote1104 = new Remote1104Form();
            remote1104.ShowDialog();
        }

        private void AbortScanningBtn_Click(object sender, EventArgs e)//似乎原点被改动了~，当前X坐标被置0了，Y坐标没变。。。。需要再看看~~~！
        {
            try
            {
                if (GlobalVariables.ScanningConfiguration == Constant.ONE_PLUS_ONE)
                {
                    ScanningThread1.Abort();
                }

                if (GlobalVariables.ScanningConfiguration == Constant.ONE_PLUS_TWO)
                {
                    ScanningThread2.Abort();
                }

                Thread.Sleep(500);

                //停掉电机
                Dev.MDriverX.InstantaneousStop();
                Dev.MDriverY.InstantaneousStop();

                //因为Stop函数只要执行一条写命令，所以就没有必要将其放到线程里面去了。
                Dev.DownstreamOPC1.InstrumentStop();
                Dev.UpstreamOPC.InstrumentStop();

                //停掉上下游的泵
                PLC.Close(Constant.SSR_PUMP_UPSTREAM);
                PLC.Close(Constant.SSR_PUMP_DOWNSTREAM1);
            }
            catch (System.Exception ex)
            {
                GlobalVariables.log.Error("Error occured at AbortScanningBtn_Click Function...." + ex.ToString());
            }
        }

        private void FilterTestParametersBtn_Click(object sender, EventArgs e)
        {
            FilterTestParametersForm filterTestParametersForm = new FilterTestParametersForm();
            filterTestParametersForm.ShowDialog();
            MPPSTBox.Text = GlobalVariables.strMPPS;

            /***********************计算扫描过程所需要的参数************************/
            FaceScanningXBlockNumber = Convert.ToUInt32(Math.Ceiling(Filter.FoldingLength / Probe.SettingVelocity));    //扫描的列数，对应扫描矩阵的列
            Probe.ActualVelocity = 1.0 * Filter.FoldingLength / FaceScanningXBlockNumber;         //此为实际的扫描速度，单位为 mm/s
            FaceScanningYBlockNumber = Convert.ToUInt32(Math.Ceiling(Filter.FoldingWidth / Probe.bp));     //扫描的行数，对应扫描矩阵的行
            /************************************************************************/
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            if (GlobalVariables.RunningMode == RUNNING_MODE.REAL)
            {
                Graphics g = e.Graphics;
                g.DrawImage(bkBitmap, 450, 225);  //指定bkBitmap在整个Form上的位置，X，Y为图像在Form左上角的起始位置          
                base.OnPaint(e);
            }
            else if (GlobalVariables.RunningMode == RUNNING_MODE.SIMULATION)
            {
                Graphics g = e.Graphics;
                g.DrawImage(bkBitmap, 450, 100);  //指定bkBitmap在整个Form上的位置，X，Y为图像在Form左上角的起始位置          
                base.OnPaint(e);
            }
        }

        private void LabelPrintBtn_Click(object sender, EventArgs e)
        {
            LabelPrintForm labelPrintForm = new LabelPrintForm();
            labelPrintForm.ShowDialog();
        }
        public void DrawXAxis(Graphics g)//画X轴。可否加入2个新的参数来确定新坐标系的原点在框体中的位置，或者加入一个点变量？方便调用画坐标。
        {
            Pen myPen = new Pen(Color.Green, 3);//坐标颜色
            Pen axisPen = new Pen(Color.Green, 3);
            Pen myAxisPen = new Pen(Color.Green, 3);//坐标刻度文字颜色

            System.Drawing.Drawing2D.AdjustableArrowCap ac;
            ac = new System.Drawing.Drawing2D.AdjustableArrowCap(4, 4);
            myAxisPen.CustomEndCap = ac;//轴坐标的末端为箭头
            
            g.DrawLine(myAxisPen, Constant.REF_POS_X, Constant.REF_POS_Y, Constant.REF_POS_X + Convert.ToInt32(Filter.FoldingLength*kZoomFactor) + 50, Constant.REF_POS_Y);
            Font theAxisFont = new Font("Arial", 12);
            SolidBrush myBrush = new SolidBrush(Color.Green);
            //g.DrawString("X", theAxisFont, myBrush, ClientRectangle.Right - 600, ClientRectangle.Top - 50 + kYAxisIndent + 50);//X轴文字，暂时先不用管它


            float x1, y1, x2, y2;

            float BlockLength = Convert.ToSingle(Probe.ActualVelocity*kZoomFactor);

            for (int i = 0; i <= FaceScanningXBlockNumber; i++)
            {
                x1 = Constant.REF_POS_X + i*BlockLength;
                y1 = Constant.REF_POS_Y;
                x2 = Constant.REF_POS_X + i*BlockLength;
                y2 = Constant.REF_POS_Y + 10;
                g.DrawLine(myPen, x1, y1, x2, y2);

                x1 = Constant.REF_POS_X + i*BlockLength - 10;
                y1 = Constant.REF_POS_Y + 12; 
                g.DrawString(i.ToString(), theAxisFont, myBrush, x1, y1);
            }

            myPen.Dispose();
            myAxisPen.Dispose();
            myBrush.Dispose();
            theAxisFont.Dispose();
        }

        
        public void DrawYAxis(Graphics g)//画Y轴
        {


            SolidBrush myBrush = new SolidBrush(Color.Green);
            Pen myPen = new Pen(Color.Green, 3);
            Pen myAxisPen = new Pen(Color.Green, 3);//坐标刻度文字颜色
            System.Drawing.Drawing2D.AdjustableArrowCap ac;
            ac = new System.Drawing.Drawing2D.AdjustableArrowCap(4, 4);

            myAxisPen.CustomEndCap = ac;//轴坐标的末端为箭头
            Font theAxisFont = new Font("Arial", 12);
            g.DrawLine(myAxisPen, Constant.REF_POS_X, Constant.REF_POS_Y, Constant.REF_POS_X, Constant.REF_POS_Y - Convert.ToInt32(Filter.FoldingWidth*kZoomFactor) - 50);

            float x1, y1, x2, y2;

            float BlockWidth = Convert.ToSingle(Probe.bp * kZoomFactor);

            for (int i = 0; i <= FaceScanningYBlockNumber; i++)
            {
                if (i == FaceScanningYBlockNumber)
                {
                    x1 = Constant.REF_POS_X;
                    y1 = Convert.ToSingle(Constant.REF_POS_Y - Filter.FoldingWidth*kZoomFactor);
                    x2 = Constant.REF_POS_X - 10;
                    y2 = Convert.ToSingle(Constant.REF_POS_Y - Filter.FoldingWidth * kZoomFactor);
                    g.DrawLine(myPen, x1, y1, x2, y2);

                    x1 = Constant.REF_POS_X - 30;
                    y1 = Convert.ToSingle(Constant.REF_POS_Y - Filter.FoldingWidth * kZoomFactor - 10);
                    g.DrawString(i.ToString(), theAxisFont, myBrush, x1, y1);
                }
                else
                {
                    x1 = Constant.REF_POS_X;
                    y1 = Constant.REF_POS_Y - i * BlockWidth;
                    x2 = Constant.REF_POS_X - 10;
                    y2 = Constant.REF_POS_Y - i * BlockWidth;
                    g.DrawLine(myPen, x1, y1, x2, y2);

                    x1 = Constant.REF_POS_X  - 30;
                    y1 = Constant.REF_POS_Y - i*BlockWidth - 10;
                    g.DrawString(i.ToString(), theAxisFont, myBrush, x1, y1);
                }

            }
        }

        private void VerifyLeakPointsBtn_Click_1(object sender, EventArgs e)
        {
            //必须把确认漏点的代码放到线程中去，否则程序运行极不正常，搞不懂哪里的错误
            //现在程序运行正常了，至少是模拟测试正常了。
            VerifyLeakPointsScanningThread = new Thread(VerifyLeakPointsScanning);
            VerifyLeakPointsScanningThread.Start();
        }

        

        private void LeakBlockDistributionBtn_Click(object sender, EventArgs e)
        {

            GlobalVariables.bkLeakBitmap = new Bitmap(1050, 700);
            GlobalVariables.bkLeakGraphics = Graphics.FromImage((System.Drawing.Image)GlobalVariables.bkLeakBitmap);


            /******************绘制过滤器的边框**********************************/
            rectangleSize = new Size((int)(Filter.FoldingLength * kZoomFactor), (int)(Filter.FoldingWidth * kZoomFactor));//过滤器边界
            rectangleTopLeft.X = 10;
            rectangleTopLeft.Y = 10;

            Rectangle RectangleArea = new Rectangle(rectangleTopLeft, rectangleSize);//过滤器图形示意图
            GlobalVariables.bkLeakGraphics.DrawRectangle(bluePen, RectangleArea);
            /************************************************************************/

            SolidBrush myBrush = new SolidBrush(Color.Blue);
            GlobalVariables.bkLeakGraphics.FillRectangle(myBrush, RectangleArea);

            myBrush.Color = Color.Red;


            PointF CurrentLocationF = new PointF();

            sizeF = new SizeF(Convert.ToSingle(Probe.ActualVelocity) * Convert.ToSingle(kZoomFactor), Convert.ToSingle(Probe.bp) * Convert.ToSingle(kZoomFactor));

            RectangleF BlockRect = new RectangleF();

            PointF refBottomLeftLocation = new PointF();
            refBottomLeftLocation.X = 10.0f;
            refBottomLeftLocation.Y = 10.0f + rectangleSize.Height;

            foreach (ScanDataBlock pl in DataTable.PossibleLeakBlocks)
            {
                CurrentLocationF.X = refBottomLeftLocation.X + pl.column * sizeF.Width;
                CurrentLocationF.Y = refBottomLeftLocation.Y - (pl.row + 1) * sizeF.Height;

                BlockRect = new RectangleF(CurrentLocationF, sizeF);
                GlobalVariables.bkLeakGraphics.FillRectangle(myBrush, BlockRect);
            }

            LeakBlockPointForm leakBlockPointForm = new LeakBlockPointForm();
            leakBlockPointForm.ShowDialog();      

        }



        private void FinalLeakPointsDistributionBtn_Click(object sender, EventArgs e)
        {

            GlobalVariables.bkLeakBitmap = new Bitmap(1050, 700);   //这个是一个画布
            GlobalVariables.bkLeakGraphics = Graphics.FromImage((System.Drawing.Image)GlobalVariables.bkLeakBitmap);


            /******************绘制过滤器的边框**********************************/
            rectangleSize = new Size((int)(Filter.FoldingLength * kZoomFactor), (int)(Filter.FoldingWidth * kZoomFactor));//过滤器边界
            rectangleTopLeft.X = 10;
            rectangleTopLeft.Y = 10;

            Rectangle RectangleArea = new Rectangle(rectangleTopLeft, rectangleSize);//过滤器图形示意图
            GlobalVariables.bkLeakGraphics.DrawRectangle(bluePen, RectangleArea);
            /************************************************************************/

            SolidBrush myBrush = new SolidBrush(Color.Blue);
            GlobalVariables.bkLeakGraphics.FillRectangle(myBrush, RectangleArea);

            myBrush.Color = Color.Red;


            PointF CurrentLocationF = new PointF();


            sizeF = new SizeF(Convert.ToSingle(Probe.ap) * Convert.ToSingle(kZoomFactor), Convert.ToSingle(Probe.bp) * Convert.ToSingle(kZoomFactor));

            RectangleF PointRect = new RectangleF();


            PointF refBottomLeftLocation = new PointF();
            refBottomLeftLocation.X = 10.0f;
            refBottomLeftLocation.Y = 10.0f + rectangleSize.Height;


            foreach (ScanDataBlock pl in DataTable.VerifiedLeakPoints)
            {
                CurrentLocationF.X = Convert.ToSingle(refBottomLeftLocation.X + (pl.XPos - 0*Probe.ap/2.0)*kZoomFactor);
                CurrentLocationF.Y = Convert.ToSingle(refBottomLeftLocation.Y - (pl.YPos + 0*Probe.bp/2.0)*kZoomFactor);

                PointRect = new RectangleF(CurrentLocationF, sizeF);
                GlobalVariables.bkLeakGraphics.FillRectangle(myBrush, PointRect);
            }

            LeakPointsForm leakPointsForm = new LeakPointsForm();
            leakPointsForm.ShowDialog();
        }

        private void IntegralEfficiencyBtn_Click(object sender, EventArgs e)
        {
            GlobalVariables.IntegralEfficiency = 0.0;
            GlobalVariables.UpstreamConcentration = 0.0;
            GlobalVariables.MinimumEfficiency95 = 0.0;

            double TotalUpstreamPN = 0.0, TotalDownstreamPN = 0.0;

            for (int row = 0; row < FaceScanningYBlockNumber; row++)
                for (int col = 0; col < FaceScanningXBlockNumber; col++)
                {
                    TotalDownstreamPN += DownScanningData[row, col].DownstreamRaw95PN;
                    TotalUpstreamPN += DownScanningData[row, col].UpstreamRaw95PN;
                }

            GlobalVariables.IntegralEfficiency = 1 - (TotalDownstreamPN / TotalUpstreamPN);
            GlobalVariables.UpstreamConcentration = TotalUpstreamPN / (FaceScanningXBlockNumber * FaceScanningYBlockNumber)/(28.3*1000);


            double MinUpstreamPN, MaxDownstreamPN;
            MinUpstreamPN = DownScanningData[0, 0].UpstreamRaw95PN;
            MaxDownstreamPN = DownScanningData[0, 0].DownstreamRaw95PN;
            
            for (int row = 0; row < FaceScanningYBlockNumber; row++)
                for (int col = 0; col < FaceScanningXBlockNumber; col++)
                {
                    if (DownScanningData[row, col].DownstreamRaw95PN > MaxDownstreamPN)
                    {
                        MaxDownstreamPN = DownScanningData[row, col].DownstreamRaw95PN;
                    }

                    if (DownScanningData[row, col].UpstreamRaw95PN < MinUpstreamPN)
                    {
                        MinUpstreamPN = DownScanningData[row, col].UpstreamRaw95PN;
                    }
                }

            GlobalVariables.MinimumEfficiency95 = 1 - MaxDownstreamPN / MinUpstreamPN;
         
            double IntergralEfficiency = GlobalVariables.IntegralEfficiency;
            ClassificationToTestResult = "";
            if (IntergralEfficiency<0.85)
            {
                ClassificationToTestResult = "G";
            }
            else if (IntergralEfficiency<0.95)
            {
                ClassificationToTestResult = "H10";
            }
            else if (IntergralEfficiency<0.995)
            {
                ClassificationToTestResult = "H11";
            }
            else if (IntergralEfficiency < 0.9995)
            {
                ClassificationToTestResult = "H12";
            }
            else if (IntergralEfficiency < 0.99995)
            {
                ClassificationToTestResult = "H13";
            }
            else if (IntergralEfficiency < 0.999995)
            {
                ClassificationToTestResult = "H14";
            }
            else if (IntergralEfficiency < 0.9999995)
            {
                ClassificationToTestResult = "U15";
            }
            else if (IntergralEfficiency < 0.99999995)
            {
                ClassificationToTestResult = "U16";
            }
            else if (IntergralEfficiency < 0.999999995)
            {
                ClassificationToTestResult = "U17";
            }
            else
            {
                ClassificationToTestResult = "U17";
                MessageBox.Show("some where maybe wrong, or it's so peferct!");
            }

            MessageBox.Show("IntegralEfficiency is:" + GlobalVariables.IntegralEfficiency.ToString("N10") + "\n" + TotalDownstreamPN.ToString() + "\n" + TotalUpstreamPN.ToString()+"\n"+ClassificationToTestResult);
        }

        private void ResultReportBtn_Click(object sender, EventArgs e)
        {
            Thread ResultReportThread = new Thread(ResultReport);
            ResultReportThread.Start();
        }

        


        static void SetPictureToRange(Excel._Workbook p_Workbook, Excel._Worksheet p_Worksheet, Excel.Range v_Range, string fileName)
        {
            try
            {
                Excel.Pictures v_Pictures = (Excel.Pictures)p_Worksheet.Pictures(Type.Missing);
                Excel.Picture v_Picture = v_Pictures.Insert(fileName, Type.Missing);

                // Excel的get_Range方法可以得到Excel的单元格，可以用来设置图片显示的位置
                //Excel.Range v_Range = p_Worksheet.get_Range(p_strRangeName, Type.Missing);
                double v_fFactor = 1;

                //设置图片大小
                if (v_Picture.Width * (double)v_Range.Height > v_Picture.Height * (double)v_Range.Width)
                    v_fFactor = (double)v_Range.Width / (double)v_Picture.Width;
                else
                    v_fFactor = (double)v_Range.Height / (double)v_Picture.Height;

                v_Picture.Left = (double)v_Range.Left + ((double)v_Range.Width - (v_Picture.Width * v_fFactor)) / 2 + 1;
                v_Picture.Top = (double)v_Range.Top + ((double)v_Range.Height - (v_Picture.Height * v_fFactor)) / 2 + 1;
                v_Picture.Width = v_Picture.Width * v_fFactor - 0.5d;
                v_Picture.Height = v_Picture.Height * v_fFactor - 0.5d;
            }
            catch (System.Exception ex)
            {
                MessageBox.Show(ex.Message + "in the SetPictureToRange(...) function");
            }
        }

        private void Manual_VerifyBtn_Click(object sender, EventArgs e)
        {
            MessageBox.Show("考虑加入");
            //简单的话，其实打开一个TXT，可以人工记录当前坐标即可。~~~~
        }

        private void GoToOriginBtn_Click_1(object sender, EventArgs e)
        {
            Dev.MDriverX.AccelerationSetting();
            Dev.MDriverX.DecelerationSetting();

            Dev.MDriverX.JogC(400, Constant.DIR321);   //!!!!

            
            Dev.MDriverY.AccelerationSetting();
            Dev.MDriverY.DecelerationSetting();

            Dev.MDriverY.JogC(400, Constant.DIR123);

            //设定扫描的起始点
            Scanner.CurrentX = 0;
            Scanner.CurrentY = 0;
        }

        private void GoToScanningStartPointBtn_Click(object sender, EventArgs e)
        {
            Scanner.MoveTo(0, 0);
        }

        private void SetScanningStartPointBtn_Click(object sender, EventArgs e)
        {
            Scanner.CurrentX = 0;
            Scanner.CurrentY = 0;
        }

        private void CloseToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void GoToOriginToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Dev.MDriverX.AccelerationSetting();
            Dev.MDriverX.DecelerationSetting();

            Dev.MDriverX.JogC(400, Constant.DIR321);   //!!!!


            Dev.MDriverY.AccelerationSetting();
            Dev.MDriverY.DecelerationSetting();

            Dev.MDriverY.JogC(400, Constant.DIR123);

            //设定扫描的起始点
            Scanner.CurrentX = 0;
            Scanner.CurrentY = 0;
        }

        private void GoToScanningStartPointToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Thread GoToScanningStartPointThread = new Thread(GoToScanningStartPoint);
            GoToScanningStartPointThread.Start();
        }

        private void GoToScanningStartPoint()
        {
            Scanner.MoveTo(0, 0);
        }

        private void SetScanningStartPointToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Scanner.CurrentX = 0;
            Scanner.CurrentY = 0;
        }

        private void FilterTestParametersToolStripMenuItem_Click(object sender, EventArgs e)
        {
            FilterTestParametersForm filterTestParametersForm = new FilterTestParametersForm();
            filterTestParametersForm.ShowDialog();
            MPPSTBox.Text = GlobalVariables.strMPPS;

            /***********************计算扫描过程所需要的参数************************/
            FaceScanningXBlockNumber = Convert.ToUInt32(Math.Ceiling(Filter.FoldingLength / Probe.SettingVelocity));    //扫描的列数，对应扫描矩阵的列
            Probe.ActualVelocity = 1.0 * Filter.FoldingLength / FaceScanningXBlockNumber;         //此为实际的扫描速度，单位为 mm/s
            FaceScanningYBlockNumber = Convert.ToUInt32(Math.Ceiling(Filter.FoldingWidth / Probe.bp));     //扫描的行数，对应扫描矩阵的行
            /************************************************************************/
        }

        private void ScanningPreparingToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AuxScanningForm auxScanningForm = new AuxScanningForm();
            auxScanningForm.ShowDialog();
        }

        private void FrameScanToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //Thread FilterScanningThread = new Thread(FilterScanning);  //这里如果用线程的话会有问题，程序运行不正常
            //FilterScanningThread.Start();

            FilterScanning();    
        }

        private void FilterScanning()
        {
            try
            {   
                //ScanningDataChart.ChartAreas["ChartAreas1"].AxisX.MajorGrid.Interval = FaceScanningXBlockNumber;
                if (GlobalVariables.RunningMode == RUNNING_MODE.REAL)
                {
                    bool IsXServoON, IsYServoON;

                    IsXServoON = Dev.MDriverX.IsServoON;
                    Thread.Sleep(50);

                    IsYServoON = Dev.MDriverY.IsServoON;
                    Thread.Sleep(50);

                    if ((!IsXServoON) || (!IsYServoON))
                    {
                        IsXServoON = Dev.MDriverX.IsServoON;
                        Thread.Sleep(50);

                        IsYServoON = Dev.MDriverY.IsServoON;
                        Thread.Sleep(50);

                        if ((!IsXServoON) || (!IsYServoON))
                        {
                            IsXServoON = Dev.MDriverX.IsServoON;
                            Thread.Sleep(50);

                            IsYServoON = Dev.MDriverY.IsServoON;
                            Thread.Sleep(50);

                            if ((!IsXServoON) || (!IsYServoON))
                            {
                                MessageBox.Show("请首先使能两个伺服电机，然后再按过滤器扫描按钮！");
                                return;
                            }
                        }
                    }
                }
                else if (GlobalVariables.RunningMode == RUNNING_MODE.SIMULATION)
                {
                    //Do Nothing......
                }

                if (Scanner.CurrentX != 0 || Scanner.CurrentY != 0)//double类型与0比较是不是会不太保险？
                {
                    MessageBox.Show("目前扫描头不在原点，请先回原点，然后再进行扫描！");
                    return;
                }

                if (GlobalVariables.ScanningConfiguration == Constant.ONE_PLUS_ONE)
                {
                    PreparingScanning1();

                    //Thread.Sleep(1000);

                    ScanningThread1 = new Thread(Scanning1);
                    ScanningThread1.Start();
                }

                if (GlobalVariables.ScanningConfiguration == Constant.ONE_PLUS_TWO)
                {
                    PreparingScanning2();
                    ScanningThread2 = new Thread(Scanning2);
                    ScanningThread2.Start();
                }

            }
            catch (System.Exception ex)
            {
                GlobalVariables.log.Error("Error ocured @ function FrameScanBtn_Click in ScannerForm.cs" + ex.ToString());
            }

        }

        private void VerifyLeakPointsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //必须把确认漏点的代码放到线程中去，否则程序运行极不正常，搞不懂哪里的错误
            //现在程序运行正常了，至少是模拟测试正常了。
            VerifyLeakPointsScanningThread = new Thread(VerifyLeakPointsScanning);
            VerifyLeakPointsScanningThread.Start();
        }

        private void VerifyLeakPointsScanning()
        {

            try
            {
                //将扫描模式设定为LEAK_POINT_VERIFY_STATUS，这样在粒子计数器的时间响应中回去执行相关的代码。
                ScanningStatus = LEAK_POINT_VERIFY_STATUS;

                counts_of_responces = 0;
                string str = "";
                //DebugInfoTBox.Text = "";
                
                Scanner.MoveTo(0, 0);

                Scanner.CurrentX = 0.0;
                Scanner.CurrentY = 0.0;

                int TotalPossibleLeakBlocks = 0;
                int VerifiedLeakBlocks = 0;

                TotalPossibleLeakBlocks = DataTable.PossibleLeakBlocks.Count;
                TotalPossibleLeakBlocksTBox.Text = TotalPossibleLeakBlocks.ToString();

                VerifyingPBar.Visible = true;
                VerifyingPBar.Minimum = 1;

                if (TotalPossibleLeakBlocks == 0)
                {
                    VerifyingPBar.Maximum = 1;
                }
                else
                {
                    VerifyingPBar.Maximum = TotalPossibleLeakBlocks;
                }
                VerifyingPBar.Value = 1;
                VerifyingPBar.Step = 1;

                //在确认漏点的过程中，不需要图像表示，只要显示当前当前点的下游粒子数和判漏极限值即可了
                //但是每个漏点的实际坐标位置要记录好，以便于最后输出报表用。

                //启动上游粒子计数器
                Dev.UpstreamOPC.SimpleConfigure();
                Thread.Sleep(1000);
                Dev.UpstreamOPC.InstrumentStart();

                Dev.DownstreamOPC1.SimpleConfigure();
                Thread.Sleep(1000);

                VerifiedLeakBlocks = 0;


                //下面的代码是防止重复扫描
                ScanDataBlock VerifiedPossibleLeakBlock = new ScanDataBlock();
                VerifiedPossibleLeakBlock.row = 0;
                VerifiedPossibleLeakBlock.column = 0;

                foreach (ScanDataBlock possibleLeakBlock in DataTable.PossibleLeakBlocks)
                {
                    //为了简化问题，我们现在仅处理  当前漏点的Block和潜在的扫描方向后面的一个Block，即如果我们在第一次连续扫描时检测到的可能漏点，那确认漏点时，我们除了确认当前
                    //Block块之外，我们还得要确认与它邻近的，扫描时间上发生在它之前的那个Block块。目前我们仅处理这样两个Block块的情形。

                    if ((possibleLeakBlock.row % 2) == 0)   //偶数行的处理，因为偶数行的扫描方向都是DIR123，根据粒子计数器的传输延迟，漏点发生在扫描方向的后面
                    {
                        if (possibleLeakBlock.column == 0) //如果可能漏的Block块发生在偶数行的开始，此时因为它之前没有Block块，这种情况下我们仅处理一个Block块
                        {
                            FaceScanningXPointNumber = Convert.ToUInt32(Math.Ceiling(Probe.ActualVelocity / Probe.ap));//!!!!是否加入如c++里一样的小数处理避免整数丢失？？
                            //FaceScanningXPointNumberTBox.Text = FaceScanningXPointNumber.ToString();

                            //这里的坐标将来就作为输出到报表中的坐标值。。。。。。
                            PossibleLeakPoint.XPos = 0;
                            PossibleLeakPoint.YPos = possibleLeakBlock.row * Probe.bp;

                            //移动到需要确认漏点Block块的起始位置，此种情况是1个Block块，因为漏块在最边缘
                            Scanner.MoveTo(PossibleLeakPoint.XPos, PossibleLeakPoint.YPos);

                            VerifiedPossibleLeakBlock.row = possibleLeakBlock.row;
                            VerifiedPossibleLeakBlock.column = possibleLeakBlock.column;

                            str += "MoveTo (" + PossibleLeakPoint.XPos.ToString() + "," + PossibleLeakPoint.YPos.ToString() + ")\n";
                        }
                        else if (possibleLeakBlock.column == (FaceScanningXBlockNumber - 1))  //如果可以的Block为扫描方向上的最后一个Block，则放弃确认处理，避免出现坐标混乱问题
                        {
                            FaceScanningXPointNumber = 0;
                        }
                        else    //否则的话我们需要检查两个Block块：当前的和它之前的
                        {
                            if ((possibleLeakBlock.row == VerifiedPossibleLeakBlock.row) && (possibleLeakBlock.column == (VerifiedPossibleLeakBlock.column + 1)))
                            {
                                FaceScanningXPointNumber = Convert.ToUInt32(Math.Ceiling(Probe.ActualVelocity / Probe.ap));
                                //FaceScanningXPointNumberTBox.Text = FaceScanningXPointNumber.ToString();

                                //这里的坐标将来就作为输出到报表中的坐标值。。。。。。
                                PossibleLeakPoint.XPos = possibleLeakBlock.column * Probe.ActualVelocity;
                                PossibleLeakPoint.YPos = possibleLeakBlock.row * Probe.bp;
                            }
                            else
                            {
                                //当前Block和前一个Block总共需要检查的漏点数。
                                FaceScanningXPointNumber = 2 * Convert.ToUInt32(Math.Ceiling(Probe.ActualVelocity / Probe.ap));

                                //这里的坐标将来就作为输出到报表中的坐标值。。。。。。
                                PossibleLeakPoint.XPos = (possibleLeakBlock.column - 1) * Probe.ActualVelocity;
                                PossibleLeakPoint.YPos = possibleLeakBlock.row * Probe.bp;
                            }

                            //移动到需要确认漏点Block块的起始位置，此种情况是2个Block块
                            Scanner.MoveTo(PossibleLeakPoint.XPos, PossibleLeakPoint.YPos);

                            //下面的变量传递行列信息到DebugInfoTBox信息框中......,如果是检查两个Block的情况，可能只是给出当前一个Block的行列信息
                            //Block的行列和坐标的信息或许不是精确对应，。。。。。
                            PossibleLeakPoint.row = possibleLeakBlock.row;
                            PossibleLeakPoint.column = possibleLeakBlock.column;

                            VerifiedPossibleLeakBlock.row = possibleLeakBlock.row;
                            VerifiedPossibleLeakBlock.column = possibleLeakBlock.column;

                            str += "MoveTo (" + PossibleLeakPoint.XPos.ToString() + "," + PossibleLeakPoint.YPos.ToString() + ")\n";
                        }                       

                        for (uint i = 0; i < FaceScanningXPointNumber; i++)//确认Block里的露点
                        {
                            DownOPC1DataCount = 0;
                            Dev.DownstreamOPC1.PredefinedSampleCount = 1;
                            Dev.DownstreamOPC1.InstrumentStart();

                            while (DownOPC1DataCount != 1)
                            {
                                Thread.Sleep(100);
                            }

                            //完成第一个扫描点后步进到下一个点进行定点扫描
                            Scanner.MoveX(Convert.ToInt32(Probe.ap), Probe.ActualVelocity, Constant.DIR123);

                            //同时要调整PossibleLeakPoint的数值，因为在同一行，所以只需要调整XPos的数值即可，增加一个扫描探头的扫描方向尺寸
                            PossibleLeakPoint.XPos += Probe.ap;

                        }

                        VerifiedLeakBlocks++;
                        VerifiedLeakBlocksTBox.Text = VerifiedLeakBlocks.ToString();

                        VerifyingPBar.PerformStep();
                    }
                    else    //奇数行的处理，因为奇数行的扫描方向都是DIR321，根据粒子计数器的传输延迟，漏点发生在扫描方向的后面
                    {
                        if (possibleLeakBlock.column == (FaceScanningXBlockNumber - 1)) //如果可能漏的Block块发生在奇数行的开始，此时因为它之前没有Block块，这种情况下我们仅处理一个Block块~~！！！
                        {

                            FaceScanningXPointNumber = Convert.ToUInt32(Math.Ceiling(Probe.ActualVelocity / Probe.ap));
                            //FaceScanningXPointNumberTBox.Text = FaceScanningXPointNumber.ToString();

                            //这里的坐标将来就作为输出到报表中的坐标值。。。。。。
                            PossibleLeakPoint.XPos = Filter.FoldingLength;
                            PossibleLeakPoint.YPos = possibleLeakBlock.row * Probe.bp;

                            //移动到需要确认漏点Block块的起始位置，此种情况是1个Block块，因为漏块在最边缘
                            Scanner.MoveTo(PossibleLeakPoint.XPos, PossibleLeakPoint.YPos);


                            VerifiedPossibleLeakBlock.row = possibleLeakBlock.row;
                            VerifiedPossibleLeakBlock.column = possibleLeakBlock.column;


                            //Thread.Sleep(5000);//!!!5秒确认到位？
                            str += "MoveTo (" + PossibleLeakPoint.XPos.ToString() + "," + PossibleLeakPoint.YPos.ToString() + ")\n";
                        }
                        else if (possibleLeakBlock.column == 0) //如果可以的Block为扫描方向上的最后一个Block，则放弃确认处理，避免出现坐标混乱问题
                        {
                            FaceScanningXPointNumber = 0;
                        }
                        else    //否则的话我们需要检查两个Block块：当前的和它之前的
                        {
                            if ((possibleLeakBlock.row == VerifiedPossibleLeakBlock.row) && (possibleLeakBlock.column == (VerifiedPossibleLeakBlock.column - 1)))
                            {
                                FaceScanningXPointNumber = Convert.ToUInt32(Math.Ceiling(Probe.ActualVelocity / Probe.ap));
                                //FaceScanningXPointNumberTBox.Text = FaceScanningXPointNumber.ToString();

                                //这里的坐标将来就作为输出到报表中的坐标值。。。。。。NOTE: 这里的坐标计算要特别谨慎。。。。。。
                                PossibleLeakPoint.XPos = (possibleLeakBlock.column) * Probe.ActualVelocity + 1 * Probe.ActualVelocity - Probe.ap;
                                PossibleLeakPoint.YPos = possibleLeakBlock.row * Probe.bp;
                            }
                            else
                            {
                                //当前Block和前一个Block总共需要检查的漏点数。
                                FaceScanningXPointNumber = 2 * Convert.ToUInt32(Math.Ceiling(Probe.ActualVelocity / Probe.ap));
                                //FaceScanningXPointNumberTBox.Text = FaceScanningXPointNumber.ToString();

                                //这里的坐标将来就作为输出到报表中的坐标值。。。。。。NOTE: 这里的坐标计算要特别谨慎。。。。。。
                                PossibleLeakPoint.XPos = (possibleLeakBlock.column + 1) * Probe.ActualVelocity + 1 * Probe.ActualVelocity - Probe.ap;
                                PossibleLeakPoint.YPos = possibleLeakBlock.row * Probe.bp;
                            }

                            //移动到需要确认漏点Block块的起始位置，此种情况是1个Block块，因为漏块在最边缘
                            Scanner.MoveTo(PossibleLeakPoint.XPos, PossibleLeakPoint.YPos);

                            //下面的变量传递行列信息到DebugInfoTBox信息框中......,如果是检查两个Block的情况，可能只是给出当前一个Block的行列信息
                            //Block的行列和坐标的信息或许不是精确对应，。。。。。
                            PossibleLeakPoint.row = possibleLeakBlock.row;
                            PossibleLeakPoint.column = possibleLeakBlock.column;

                            VerifiedPossibleLeakBlock.row = possibleLeakBlock.row;
                            VerifiedPossibleLeakBlock.column = possibleLeakBlock.column;

                            str += "MoveTo (" + PossibleLeakPoint.XPos.ToString() + "," + PossibleLeakPoint.YPos.ToString() + ")\n";
                        }

                        //MessageBox.Show("奇数行：Before for-loop.....FaceScanningXPointNumber = " + FaceScanningXPointNumber.ToString());
                        for (uint i = 0; i < FaceScanningXPointNumber; i++)
                        {

                            DownOPC1DataCount = 0;
                            Dev.DownstreamOPC1.PredefinedSampleCount = 1;
                            Dev.DownstreamOPC1.InstrumentStart();

                            while (DownOPC1DataCount != 1)
                            {
                                Thread.Sleep(100);
                            }

                            //完成第一个扫描点后步进到下一个点进行定点扫描，但要注意现在的步进方向为Constant.DIR321
                            Scanner.MoveX(Convert.ToInt32(Probe.ap), Probe.ActualVelocity, Constant.DIR321);

                            //同时要调整PossibleLeakPoint的数值，因为在同一行，所以只需要调整XPos的数值即可，增加一个扫描探头的扫描方向尺寸
                            PossibleLeakPoint.XPos -= Probe.ap;

                            //避免XPos小于零的状况出现，！！！小于零是没有意义的，仅退出这个Block的For循环。
                            if (PossibleLeakPoint.XPos <= 0)
                            {
                                //跳出当前for-loop......
                                break;
                            }
                        }
                        VerifiedLeakBlocks++;
                        VerifiedLeakBlocksTBox.Text = VerifiedLeakBlocks.ToString();

                        VerifyingPBar.PerformStep();
                    }
                }

                File.WriteAllText("MoveTo.txt", str);

                string strLeakPoints = "";
                string strTime = DateTime.Now.Hour.ToString() + "H" + DateTime.Now.Minute.ToString() + "M" + DateTime.Now.Second.ToString() + "S";

                strLeakPoints += "Scanning Information\r\n\r\n";

                strLeakPoints += "过滤器尺寸 :" + Filter.FoldingLength.ToString() + "mm" + "X" + Filter.FoldingWidth.ToString() + "mm \r\n\r\n";

                strLeakPoints += "探头实际扫描速度：" + Probe.ActualVelocity.ToString("N2") + "mm/s \r\n\r\n";

                strLeakPoints += "Block数目：" + FaceScanningXBlockNumber.ToString() + "X" + FaceScanningYBlockNumber.ToString() + "\r\n\r\n";

                strLeakPoints += "风量：" + (3669.1521 * Math.Pow(GlobalVariables.NozzleDiameter, 2) * Math.Sqrt(KIMOSensors.CurrentNozzleP)).ToString("N2") + "\r\n\r\n";

                strLeakPoints += "Verified Leak Points\r\n\r\n\r\n";

                strLeakPoints += "Total Verified Leak Points is " + DataTable.VerifiedLeakPoints.Count.ToString() + "\r\n\r\n";

                foreach (ScanDataBlock sdBlock in DataTable.VerifiedLeakPoints)
                {
                    strLeakPoints += "X, Y =  Block(" + sdBlock.row.ToString() + "," + sdBlock.column.ToString() + ")\tPosition(" + sdBlock.XPos.ToString("N0") + "," + sdBlock.YPos.ToString("N0") + ")"
                                          + "\t 下游粒子数与期望极限值的差：" + (sdBlock.RawPN - sdBlock.ExpectedPN).ToString() + "\r\n\r\n\r\n";
                }

                string fileName = "VerifiedLeakPoints" + strTime + ".txt";
                File.WriteAllText(fileName, strLeakPoints);

                System.Diagnostics.Process.Start(fileName);

                //将扫描模式设定为IDLE_STATUS，这样在粒子计数器的时间响应中回去执行相关的代码。
                ScanningStatus = IDLE_STATUS;

            }
            catch (System.Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        private void AbortScanningToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                if (GlobalVariables.ScanningConfiguration == Constant.ONE_PLUS_ONE)
                {
                    ScanningThread1.Abort();
                }

                if (GlobalVariables.ScanningConfiguration == Constant.ONE_PLUS_TWO)
                {
                    ScanningThread2.Abort();
                }

                Thread.Sleep(500);

                //停掉电机
                Dev.MDriverX.InstantaneousStop();
                Dev.MDriverY.InstantaneousStop();

                //因为Stop函数只要执行一条写命令，所以就没有必要将其放到线程里面去了。
                Dev.DownstreamOPC1.InstrumentStop();
                Dev.UpstreamOPC.InstrumentStop();

                //停掉上下游的泵
                PLC.Close(Constant.SSR_PUMP_UPSTREAM);
                PLC.Close(Constant.SSR_PUMP_DOWNSTREAM1);
            }
            catch (System.Exception ex)
            {
                GlobalVariables.log.Error("Error occured at AbortScanningBtn_Click Function...." + ex.ToString());
            }
        }

        private void ReFreshToolStripMenuItem_Click(object sender, EventArgs e)
        {
            DataTable.PossibleLeakBlocks.Clear();
            DataTable.VerifiedLeakPoints.Clear();

            //GlobalVariables.strDebugInfo = "";
            //DebugInfoTBox.Text = GlobalVariables.strDebugInfo;

            VerifiedLeakBlocksTBox.Text = "";
            TotalPossibleLeakBlocksTBox.Text = "";

            VerifyingPBar.Visible = false;


            //重绘过滤器扫描示意图
            bkBitmap = new Bitmap(Constant.BK_BITMAP_WIDTH, Constant.BK_BITMAP_HEIGHT);
            bkGraphics = Graphics.FromImage((System.Drawing.Image)bkBitmap);

            //Draw Filter
            rectangleSize = new Size((int)(XScanningLength * kZoomFactor), (int)(Filter.FoldingWidth * kZoomFactor));//过滤器边界
            rectangleTopLeft.X = Constant.REF_POS_X;
            rectangleTopLeft.Y = Constant.REF_POS_Y - rectangleSize.Height;

            Rectangle RectangleArea = new Rectangle(rectangleTopLeft, rectangleSize);//过滤器图形示意图
            bkGraphics.DrawRectangle(bluePen, RectangleArea);

            //DrawAxis
            DrawXAxis(bkGraphics);
            DrawYAxis(bkGraphics);

            RealTimeLocationF.X = Constant.REF_POS_X;
            RealTimeLocationF.Y = Constant.REF_POS_Y;

            RealTimeLocationF.X -= Convert.ToSingle(Probe.ActualVelocity) * Convert.ToSingle(kZoomFactor);
            RealTimeLocationF.Y -= Convert.ToSingle(Probe.bp) * Convert.ToSingle(kZoomFactor);

            DownScanningData = new ScanDataBlock[FaceScanningYBlockNumber, FaceScanningXBlockNumber];
            UpScanningData = new ScanDataBlock[FaceScanningYBlockNumber, FaceScanningXBlockNumber];

            Invalidate();

            GlobalVariables.DownOPC1RunningCount = 0;
            GlobalVariables.DownOPC1ResponseEventCount = 0;

            GlobalVariables.strMotorRunningInfo = "";
            Dev.DownstreamOPC1.strOPCRunningInfo = "";
            GlobalVariables.strDownOPC1EventInfo = "";
            GlobalVariables.strUpOPCEventInfo = "";

            //在ReFresh按钮的代码块里面重新初始化数据来重绘ScanningDataChart.........
            ScanningDataChart.Series["Upstream"].Points.Clear();
            ScanningDataChart.Series["Downstream"].Points.Clear();
            ScanningDataChart.Series["Criterion"].Points.Clear();

            if (Filter.Type == "H13")
            {
                ScanningDataChart.ChartAreas[0].AxisY.Maximum = 3000;
            }
            else if (Filter.Type == "H14")
            {
                ScanningDataChart.ChartAreas[0].AxisY.Maximum = 3000;
            }
            else if (Filter.Type == "U15")
            {
                ScanningDataChart.ChartAreas[0].AxisY.Maximum = 1000;
            }
            else if (Filter.Type == "U16")
            {
                ScanningDataChart.ChartAreas[0].AxisY.Maximum = 300;
            }
            else if (Filter.Type == "U17")
            {
                ScanningDataChart.ChartAreas[0].AxisY.Maximum = 200;
            }
            else
            {
                ScanningDataChart.ChartAreas[0].AxisY.Maximum = 5000;
            }

            for (int row = 0; row < FaceScanningYBlockNumber; row++)
            {
                for (int col = 0; col < FaceScanningXBlockNumber; col++)
                {
                    ScanningDataChart.Series["Upstream"].Points.AddY(100.0);
                    ScanningDataChart.Series["Downstream"].Points.AddY(0.0);
                    ScanningDataChart.Series["Criterion"].Points.AddY(0.0);
                }
            }

            ScanningDataChart.Series["Upstream"].Color = Color.Red;
            ScanningDataChart.Series["Downstream"].Color = Color.Blue;
            ScanningDataChart.Series["Criterion"].Color = Color.Green;

            //强制Chart图像重绘当前所有数据点
            ScanningDataChart.Invalidate();      
        }

        private void LabelPrintToolStripMenuItem_Click(object sender, EventArgs e)
        {
            LabelPrintForm labelPrintForm = new LabelPrintForm();
            labelPrintForm.ShowDialog();
        }

        private void IntegralEfficiencyToolStripMenuItem_Click(object sender, EventArgs e)
        {
            GlobalVariables.IntegralEfficiency = 0.0;
            GlobalVariables.UpstreamConcentration = 0.0;
            GlobalVariables.MinimumEfficiency95 = 0.0;

            double TotalUpstreamPN = 0.0, TotalDownstreamPN = 0.0;

            for (int row = 0; row < FaceScanningYBlockNumber; row++)
                for (int col = 0; col < FaceScanningXBlockNumber; col++)
                {
                    TotalDownstreamPN += DownScanningData[row, col].DownstreamRaw95PN;
                    TotalUpstreamPN += DownScanningData[row, col].UpstreamRaw95PN;
                }

            GlobalVariables.IntegralEfficiency = 1 - (TotalDownstreamPN / TotalUpstreamPN);
            GlobalVariables.UpstreamConcentration = TotalUpstreamPN / (FaceScanningXBlockNumber * FaceScanningYBlockNumber) / (28.3 * 1000);


            double MinUpstreamPN, MaxDownstreamPN;
            MinUpstreamPN = DownScanningData[0, 0].UpstreamRaw95PN;
            MaxDownstreamPN = DownScanningData[0, 0].DownstreamRaw95PN;

            for (int row = 0; row < FaceScanningYBlockNumber; row++)
                for (int col = 0; col < FaceScanningXBlockNumber; col++)
                {
                    if (DownScanningData[row, col].DownstreamRaw95PN > MaxDownstreamPN)
                    {
                        MaxDownstreamPN = DownScanningData[row, col].DownstreamRaw95PN;
                    }

                    if (DownScanningData[row, col].UpstreamRaw95PN < MinUpstreamPN)
                    {
                        MinUpstreamPN = DownScanningData[row, col].UpstreamRaw95PN;
                    }
                }

            GlobalVariables.MinimumEfficiency95 = 1 - MaxDownstreamPN / MinUpstreamPN;

            double IntergralEfficiency = GlobalVariables.IntegralEfficiency;
            ClassificationToTestResult = "";
            if (IntergralEfficiency < 0.85)
            {
                ClassificationToTestResult = "G";
            }
            else if (IntergralEfficiency < 0.95)
            {
                ClassificationToTestResult = "H10";
            }
            else if (IntergralEfficiency < 0.995)
            {
                ClassificationToTestResult = "H11";
            }
            else if (IntergralEfficiency < 0.9995)
            {
                ClassificationToTestResult = "H12";
            }
            else if (IntergralEfficiency < 0.99995)
            {
                ClassificationToTestResult = "H13";
            }
            else if (IntergralEfficiency < 0.999995)
            {
                ClassificationToTestResult = "H14";
            }
            else if (IntergralEfficiency < 0.9999995)
            {
                ClassificationToTestResult = "U15";
            }
            else if (IntergralEfficiency < 0.99999995)
            {
                ClassificationToTestResult = "U16";
            }
            else if (IntergralEfficiency < 0.999999995)
            {
                ClassificationToTestResult = "U17";
            }
            else
            {
                ClassificationToTestResult = "U17";
                MessageBox.Show("some where maybe wrong, or it's so peferct!");
            }

            GlobalVariables.ClassificationToTestResult = ClassificationToTestResult;//赋值到全局的变量中。
            MessageBox.Show("全效率：" + GlobalVariables.IntegralEfficiency.ToString("N10") + "\n" + "下游粒子总数：" + TotalDownstreamPN.ToString() + "\n" + "上游粒子总数：" + TotalUpstreamPN.ToString() + "\n" + ClassificationToTestResult);

        }

        private void ResultReportToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Thread ResultReportThread = new Thread(ResultReport);
            ResultReportThread.Start();
        }

        private void ResultReport()
        {
            if (1 > 0)//(GlobalVariables.IntegralEfficiency < 0.2)    //为什么要加这句判断？
            {
                //variable for Excel Operation               
                Excel.Application oXL;
                Excel._Workbook oWB;
                Excel._Worksheet oSheet;
                Excel.Range oRng;

                oXL = new Excel.Application();
                oXL.Visible = true;

                string strFileName = "";

                if (GlobalVariables.ReportLanguage == "CHINESE")
                {
                    strFileName = GlobalVariables.RootDirectory + "doc\\测试报告-EN1822.xls";
                }
                else if (GlobalVariables.ReportLanguage == "ENGLISH")  //目前还没有英文的报告 20120311
                {
                    //strFileName = GlobalVariables.RootDirectory + "doc\\ArrestanceReport_EN.xls";
                }

                oWB =
                    oXL.Workbooks.Open(strFileName,
                    Type.Missing, Type.Missing, Type.Missing, Type.Missing,
                    Type.Missing, Type.Missing, Type.Missing, Type.Missing,
                    Type.Missing, Type.Missing, Type.Missing, Type.Missing,
                    Type.Missing, Type.Missing);

                ((Excel.Worksheet)oXL.ActiveWorkbook.Sheets[1]).Select(Type.Missing);

                oSheet = (Excel.Worksheet)oWB.Sheets[1];

                oSheet.Cells[6, 1] = GlobalVariables.PartNumber;//配件号,Part no~~~
                oSheet.Cells[6, 4] = GlobalVariables.FilterType;//过滤器类型Filter Type~~~~
                oSheet.Cells[6, 7] = GlobalVariables.SerialNumber;//序列号Serial no~~~~
                oSheet.Cells[6, 10] = GlobalVariables.Today;//测试日期dataOfTest
                oSheet.Cells[6, 12] = "001";//操作员Operator~~~~


                oSheet.Cells[9, 1] = Filter.Length.ToString() + " X " + Filter.Width.ToString() + " X " + Filter.Height.ToString(); //过滤器尺寸mm*mm,Filter.dimensions
                oSheet.Cells[9, 4] = GlobalVariables.RatedFlowrate.ToString(); //额定风速m?/h,NormialFlowRate~~~~
                //oSheet.Cells[9, 7] = GlobalVariables.FinalResult.FinalPressureDrop;//初始阻力Pa,InitalPressureDrop~~~~  //这个需要跟小朱确认用哪个阻力？？？？？
                oSheet.Cells[11, 1] = Filter.Type;//过滤器等级FilterClass~~~ //Filter.Type本身就是一个字符串变量。。。。。。
                oSheet.Cells[11, 4] = Filter.OverallEfficiency.ToString();//最小全效率MinimumIntergralEfficiency~~~~    //这个地方可以用这个变量......
                oSheet.Cells[11, 7] = Filter.LocalEfficiency.ToString();//最小局部效率MinimumLocalEfficiency~~~~        //这个地方可以用这个变量......
                oSheet.Cells[11, 10] = "";//备注Comment~~~  //备注没什么实际的意义

                oSheet.Cells[15, 1] = GlobalVariables.RatedFlowrate.ToString(); //额定风量，就取这个额定风量，但是要小朱输入的时候要注意，一定不要忘记输入这个数值
                oSheet.Cells[15, 3] = GlobalVariables.TestAerosol;//测试气溶胶TestAerosol~~~
                oSheet.Cells[15, 5] = GlobalVariables.strMPPS;//粒径ParticleSize  (已经对应上相应的变量)
                oSheet.Cells[15, 7] = GlobalVariables.UpstreamConcentration;    //上游粒子浓度UpstreamConcetration  //这个可以在全效率计算的时候顺便计算出来。。。。。。
                oSheet.Cells[15, 10] = KIMOSensors.CurrentOutT.ToString();//温度Temperature
                oSheet.Cells[15, 12] = KIMOSensors.CurrentOutRH.ToString();//湿度RelHumidity

                oSheet.Cells[19, 1] = ClassificationToTestResult;//+ClassificationToTestResult;//测试得到的过滤器等级ClassificationToTestResult
                oSheet.Cells[19, 4] = GlobalVariables.IntegralEfficiency.ToString();         //全效率IntegralEfficiency
                oSheet.Cells[19, 7] = GlobalVariables.MinimumEfficiency95.ToString();        //最小效率MiniEffiency  //这个参考标准做了计算
                oSheet.Cells[19, 10] = GlobalVariables.TestAerosol;//阻力PressureDrop~~~      //这个得跟小朱商量，看看怎么来弄？

                /***************************************计算最大信号差，根据测试时上游平均浓度计算？放到全效率计算中？2012.04.22***********************************
 
                                //
                                int MaxCountLimit, MinCountLimit;

                                MaxCountLimit = Convert.ToInt32(chisquaredistr.invchisquaredistribution((ExpectedPNWithoutLeak + 1) * 2, 0.025) / 2);
                                MinCountLimit = Convert.ToInt32(chisquaredistr.invchisquaredistribution(ExpectedPNWithLeakKb * 2, 0.975) / 2);

                                int SignalValue;
                                SignalValue = MinCountLimit;

                                int SignalDifference;
                                SignalDifference = MinCountLimit - MaxCountLimit;

                                待续~~~
                ***********************************************************************************************************************/


                //oSheet.Cells[23, 1] = "455";//GlobalVariables.TestAerosol;//最大信号差MaxLeakSignal
                oSheet.Cells[21, 4] = DataTable.VerifiedLeakPoints.Count;//检测到的漏点数NumberOfLeaksDetected~~~

                if (GlobalVariables.IntegralEfficiency < Filter.OverallEfficiency)
                {
                    oSheet.Cells[21, 7] = "passed";//根据EN1822的渗漏测试结果LeakageTestToEN1822~~~
                }
                else
                {
                    oSheet.Cells[21, 7] = "didn't pass";//根据EN1822的渗漏测试结果LeakageTestToEN1822~~~
                }

                //oRng = oWB.Application.get_Range("a1", "b12");  //  .Application.get_Range("A1", missing);


                //string strTime = DateTime.Now.Hour.ToString() + "H" + DateTime.Now.Minute.ToString() + "M" + DateTime.Now.Second.ToString() + "S";
                //string mschart_data_pic_filename = "ScanningDataChart" + strTime + ".jpg";
                //ScanningDataChart.SaveImage(mschart_data_pic_filename, System.Drawing.Imaging.ImageFormat.Jpeg);

                //Thread.Sleep(1000);

                oRng = oSheet.get_Range("A25:L42", Type.Missing);
                //string asss=(@"C:\Documents and Settings\Administrator\桌面\程序验收结果(图像及数据)\20120422\09\ScanningDataChart16H14M44S.jpg");

                //SetPictureToRange(oWB, oSheet, oRng, mschart_data_pic_filename);
                SetPictureToRange(oWB, oSheet, oRng, sfdlg.FileName);            
                //SetPictureToRange(oWB, oSheet, oRng, asss);

                //漏点坐标
                int LeakPointIndex = 0;
                foreach (ScanDataBlock sdBlock in DataTable.VerifiedLeakPoints)
                {
                    LeakPointIndex++;
                    oSheet.Cells[25 + LeakPointIndex, 1] = LeakPointIndex.ToString();


                    if (sdBlock.XPos <= 0.1)
                    {
                        oSheet.Cells[25 + LeakPointIndex, 2] = "0 -- " + "30mm";
                    }
                    else if (sdBlock.XPos >= Filter.FoldingLength)
                    {
                        oSheet.Cells[25 + LeakPointIndex, 2] = (Filter.FoldingLength - 30).ToString("N0") + " -- " + Filter.FoldingLength.ToString("N0") + "mm";
                    }
                    else
                    {
                        oSheet.Cells[25 + LeakPointIndex, 2] = (sdBlock.XPos).ToString("N0") + " -- " + (sdBlock.XPos + 30).ToString("N0") + "mm";
                    }

                    if (sdBlock.YPos <= 0.1)
                    {
                        oSheet.Cells[25 + LeakPointIndex, 4] = "0 -- " + "30mm";
                    }
                    else if (sdBlock.YPos >= Filter.FoldingWidth)
                    {
                        oSheet.Cells[25 + LeakPointIndex, 4] = (Filter.FoldingWidth - 30).ToString("N0") + " -- " + Filter.FoldingWidth.ToString("N0") + "mm";
                    }
                    else
                    {
                        oSheet.Cells[25 + LeakPointIndex, 4] = (sdBlock.YPos).ToString("N0") + " -- " + (sdBlock.YPos + 30).ToString("N0") + "mm";
                    }
                    
                    oSheet.Cells[25 + LeakPointIndex, 6] = sdBlock.Penetration.ToString("N6");

                    oSheet.Cells[25 + LeakPointIndex, 8] = sdBlock.RawPN.ToString();
                    oSheet.Cells[25 + LeakPointIndex, 10] = sdBlock.ExpectedPN.ToString();
                    oSheet.Cells[25 + LeakPointIndex, 12] = (sdBlock.RawPN - sdBlock.ExpectedPN).ToString();

                }
            }
            else
            {
                MessageBox.Show("对不起，现在还没数据！");
            }
        }

        private void FinalFilterEvaluationToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //variable for Excel Operation
            Excel.Application oXL;
            Excel._Workbook oWB;
            Excel._Worksheet oSheet;
            Excel.Range oRng;

            oXL = new Excel.Application();
            oXL.Visible = true;

            string strFileName = "";

            if (GlobalVariables.ReportLanguage == "CHINESE")
            {
                strFileName = GlobalVariables.RootDirectory + "doc\\过滤器评价-EN1822.xls";
            }
            else if (GlobalVariables.ReportLanguage == "ENGLISH")
            {
                strFileName = GlobalVariables.RootDirectory + "doc\\FPReport_EN.xls";  //这个地方暂时未作修改
            }

            oWB =
                oXL.Workbooks.Open(strFileName,
                Type.Missing, Type.Missing, Type.Missing, Type.Missing,
                Type.Missing, Type.Missing, Type.Missing, Type.Missing,
                Type.Missing, Type.Missing, Type.Missing, Type.Missing,
                Type.Missing, Type.Missing);

            ((Excel.Worksheet)oXL.ActiveWorkbook.Sheets[1]).Select(Type.Missing);

            oSheet = (Excel.Worksheet)oWB.Sheets[1];

            oSheet.Cells[4, 1] = GlobalVariables.PartNumber;
            oSheet.Cells[4, 4] = Filter.Length.ToString() + "X" + Filter.Width.ToString() + "X" + Filter.Height.ToString();
            oSheet.Cells[4, 7] = GlobalVariables.strMPPS;
            oSheet.Cells[4, 10] = GlobalVariables.RatedFlowrate.ToString();

            oSheet.Cells[8, 4] = GlobalVariables.TestAerosol;

            oSheet.Cells[12, 3] = Filter.FoldingLength.ToString() + "X" + Filter.Width.ToString();

            oSheet.Cells[12, 6] = Probe.SettingVelocity.ToString();

            oSheet.Cells[17, 9] = Filter.Type;

            oSheet.Cells[18, 9] = Filter.OverallPenetration.ToString();
            oSheet.Cells[19, 9] = Filter.LocalPenetration.ToString();

            oSheet.Cells[20, 9] = Filter.Length.ToString() + "X" + Filter.Width.ToString() + "X" + Filter.Height.ToString();
            oSheet.Cells[21, 9] = Filter.FoldingLength.ToString() + "X" + Filter.Width.ToString();
            oSheet.Cells[22, 9] = GlobalVariables.RatedFlowrate.ToString();


            //上下游粒子浓度？？？？？？？？

            oSheet.Cells[29, 9] = Probe.ap.ToString() + "X" + Probe.bp.ToString();
            oSheet.Cells[30, 9] = "28.3L/Min";
            //oSheet.Cells[31, 9] = .....
            oSheet.Cells[32, 9] = Probe.SettingVelocity.ToString();

            oSheet.Cells[33, 9] = (Probe.ap / Probe.SettingVelocity).ToString("N1");

            oSheet.Cells[34, 9] = "471.6 cm^3/second";
        }

        private void FinalLeakPointsDistributionToolStripMenuItem_Click(object sender, EventArgs e)
        {

            GlobalVariables.bkLeakBitmap = new Bitmap(1050, 700);   //这个是一个画布
            GlobalVariables.bkLeakGraphics = Graphics.FromImage((System.Drawing.Image)GlobalVariables.bkLeakBitmap);


            /******************绘制过滤器的边框**********************************/
            rectangleSize = new Size((int)(Filter.FoldingLength * kZoomFactor), (int)(Filter.FoldingWidth * kZoomFactor));//过滤器边界
            rectangleTopLeft.X = 10;
            rectangleTopLeft.Y = 10;

            Rectangle RectangleArea = new Rectangle(rectangleTopLeft, rectangleSize);//过滤器图形示意图
            GlobalVariables.bkLeakGraphics.DrawRectangle(bluePen, RectangleArea);
            /************************************************************************/

            SolidBrush myBrush = new SolidBrush(Color.Blue);
            GlobalVariables.bkLeakGraphics.FillRectangle(myBrush, RectangleArea);

            myBrush.Color = Color.Red;


            PointF CurrentLocationF = new PointF();


            sizeF = new SizeF(Convert.ToSingle(Probe.ap) * Convert.ToSingle(kZoomFactor), Convert.ToSingle(Probe.bp) * Convert.ToSingle(kZoomFactor));

            RectangleF PointRect = new RectangleF();


            PointF refBottomLeftLocation = new PointF();
            refBottomLeftLocation.X = 10.0f;
            refBottomLeftLocation.Y = 10.0f + rectangleSize.Height;


            foreach (ScanDataBlock pl in DataTable.VerifiedLeakPoints)
            {
                CurrentLocationF.X = Convert.ToSingle(refBottomLeftLocation.X + (pl.XPos - 0 * Probe.ap / 2.0) * kZoomFactor);
                CurrentLocationF.Y = Convert.ToSingle(refBottomLeftLocation.Y - (pl.YPos + Probe.bp) * kZoomFactor);

                //暂时先按如下方案处理画图边界越界问题。。。。。。。。。
                if( CurrentLocationF.Y < Convert.ToSingle(refBottomLeftLocation.Y - Filter.FoldingWidth*kZoomFactor) )
                {
                    CurrentLocationF.Y = Convert.ToSingle(refBottomLeftLocation.Y - Filter.FoldingWidth * kZoomFactor); //防止画图Y方向越界
                }

                if (CurrentLocationF.X >= Convert.ToSingle(refBottomLeftLocation.X + Filter.FoldingLength * kZoomFactor))
                {
                    CurrentLocationF.X = Convert.ToSingle(refBottomLeftLocation.X + (Filter.FoldingLength - Probe.ap) * kZoomFactor);  //防止画图X方向越界
                }

                PointRect = new RectangleF(CurrentLocationF, sizeF);
                GlobalVariables.bkLeakGraphics.FillRectangle(myBrush, PointRect);
            }

            LeakPointsForm leakPointsForm = new LeakPointsForm();
            leakPointsForm.ShowDialog();
        }

        private void Remote1104ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Remote1104Form remote1104 = new Remote1104Form();
            remote1104.ShowDialog();
        }

        private void RowColum2XYToolStripMenuItem_Click(object sender, EventArgs e)
        {
            RowColum2XYForm rc2XY = new RowColum2XYForm();
            rc2XY.ShowDialog();
        }

        private void SaveImageToolStripMenuItem_Click(object sender, EventArgs e)
        {
            string str;
            //SaveFileDialog sfdlg = new SaveFileDialog();
            sfdlg.Filter = "PNG文件(*.PNG)|*.PNG|All File(*.*)|*.*";

            string strTime = DateTime.Now.Hour.ToString() + "H" + DateTime.Now.Minute.ToString() + "M" + DateTime.Now.Second.ToString() + "S";
            sfdlg.FileName = "TestResult" + strTime + ".png";

            sfdlg.ShowDialog();

            str = sfdlg.FileName;
            bkBitmap.Save(str);

            string mschart_data_pic_filename = "ScanningDataChart" + strTime + ".jpg";
            ScanningDataChart.SaveImage(mschart_data_pic_filename, System.Drawing.Imaging.ImageFormat.Jpeg);

            string str1 = System.Environment.CurrentDirectory;
            sfdlg.FileName = str1 + "\\" + mschart_data_pic_filename;//用于后面Excel表格里用
            //MessageBox.Show(sfdlg.FileName);


        }

        private void PostProcessingToolStripMenuItem_Click(object sender, EventArgs e)
        {
            DataPostProcessingForm dataPostProcessingForm = new DataPostProcessingForm();
            DialogResult dr = dataPostProcessingForm.ShowDialog();   
            
            DataTable.PossibleLeakBlocks.Clear();

            int cnt = 0;

            ScanDataBlock sdBlock = new ScanDataBlock();

            for (int row = 0; row < FaceScanningYBlockNumber; row++)
            {
                for (int column = 0; column < FaceScanningXBlockNumber; column++)
                {
                    sdBlock = DownScanningData[row, column];
                    ScanningDataChart.Series["Criterion"].Points[cnt++].YValues[0] = sdBlock.ExpectedPN * GlobalVariables.kLeakFactor;

                    if (sdBlock.RawPN > sdBlock.ExpectedPN * GlobalVariables.kLeakFactor)
                    {
                        DataTable.PossibleLeakBlocks.Add(sdBlock);
                    }
                }

                row++;

                if (row < FaceScanningYBlockNumber)
                {
                    for (int column = 0; column < FaceScanningXBlockNumber; column++)
                    {
                        sdBlock = DownScanningData[row, FaceScanningXBlockNumber - column - 1];
                        ScanningDataChart.Series["Criterion"].Points[cnt++].YValues[0] = sdBlock.ExpectedPN * GlobalVariables.kLeakFactor;

                        if (sdBlock.RawPN > sdBlock.ExpectedPN * GlobalVariables.kLeakFactor)
                        {
                            DataTable.PossibleLeakBlocks.Add(sdBlock);
                        }
                    }
                }
            }

            ScanningDataChart.Invalidate();


            string strPossibleLeakBlocks = "";
            string strTime = DateTime.Now.Hour.ToString() + "H" + DateTime.Now.Minute.ToString() + "M" + DateTime.Now.Second.ToString() + "S";

            strPossibleLeakBlocks += "Possible Leak Blocks\r\n\r\n\r\n";

            strPossibleLeakBlocks += "Total Possible Leak Blocks are " + DataTable.PossibleLeakBlocks.Count.ToString() + "\r\n\r\n";

            foreach (ScanDataBlock leakBlock in DataTable.PossibleLeakBlocks)
            {
                strPossibleLeakBlocks += "X, Y =  Block(" + leakBlock.row.ToString() + "," + leakBlock.column.ToString() + ")\r\n\r\n\r\n";
            }

            string fileName = "PossibleLeakBlocks" + strTime + ".txt";
            File.WriteAllText(fileName, strPossibleLeakBlocks);

            System.Diagnostics.Process.Start(fileName);
        }

        private void LeakBlockDistributionToolStripMenuItem_Click(object sender, EventArgs e)
        {
            GlobalVariables.bkLeakBitmap = new Bitmap(1050, 700);
            GlobalVariables.bkLeakGraphics = Graphics.FromImage((System.Drawing.Image)GlobalVariables.bkLeakBitmap);


            /******************绘制过滤器的边框**********************************/
            rectangleSize = new Size((int)(XScanningLength * kZoomFactor), (int)(Filter.FoldingWidth * kZoomFactor));//过滤器边界
            rectangleTopLeft.X = 10;
            rectangleTopLeft.Y = 10;

            Rectangle RectangleArea = new Rectangle(rectangleTopLeft, rectangleSize);//过滤器图形示意图
            GlobalVariables.bkLeakGraphics.DrawRectangle(bluePen, RectangleArea);
            /************************************************************************/

            SolidBrush myBrush = new SolidBrush(Color.Blue);
            GlobalVariables.bkLeakGraphics.FillRectangle(myBrush, RectangleArea);

            myBrush.Color = Color.Red;


            PointF CurrentLocationF = new PointF();

            sizeF = new SizeF(Convert.ToSingle(Probe.ActualVelocity) * Convert.ToSingle(kZoomFactor), Convert.ToSingle(Probe.bp) * Convert.ToSingle(kZoomFactor));

            RectangleF BlockRect = new RectangleF();

            PointF refBottomLeftLocation = new PointF();
            refBottomLeftLocation.X = 10.0f;
            refBottomLeftLocation.Y = 10.0f + rectangleSize.Height;

            foreach (ScanDataBlock pl in DataTable.PossibleLeakBlocks)
            {
                CurrentLocationF.X = refBottomLeftLocation.X + pl.column * sizeF.Width;
                CurrentLocationF.Y = refBottomLeftLocation.Y - (pl.row + 1) * sizeF.Height;

                BlockRect = new RectangleF(CurrentLocationF, sizeF);
                GlobalVariables.bkLeakGraphics.FillRectangle(myBrush, BlockRect);
            }

            LeakBlockPointForm leakBlockPointForm = new LeakBlockPointForm();
            leakBlockPointForm.ShowDialog();  
        }

        private void FPReportAutoToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Thread GenerateFPReportThread = new Thread(GenerateFPReport);
            GenerateFPReportThread.Start();
        }

        private void GenerateFPReport()
        {
            if (DataTable.FPData.Count > 0)
            {
                //variable for Excel Operation
                Excel.Application oXL;
                Excel._Workbook oWB;
                Excel._Worksheet oSheet;
                Excel.Range oRng;

                oXL = new Excel.Application();
                oXL.Visible = true;

                string strFileName = "";

                if (GlobalVariables.ReportLanguage == "CHINESE")
                {
                    strFileName = GlobalVariables.RootDirectory + "doc\\风量阻力试验报告(自动)-EN1822.xls";
                }
                else if (GlobalVariables.ReportLanguage == "ENGLISH")
                {
                    strFileName = GlobalVariables.RootDirectory + "doc\\FPReport_EN.xls";
                }

                oWB =
                    oXL.Workbooks.Open(strFileName,
                    Type.Missing, Type.Missing, Type.Missing, Type.Missing,
                    Type.Missing, Type.Missing, Type.Missing, Type.Missing,
                    Type.Missing, Type.Missing, Type.Missing, Type.Missing,
                    Type.Missing, Type.Missing);

                ((Excel.Worksheet)oXL.ActiveWorkbook.Sheets[1]).Select(Type.Missing);

                oSheet = (Excel.Worksheet)oWB.Sheets[1];

                //string strTime = DateTime.Now.ToString();全局变量里有试试看能不能直接用
                oSheet.Cells[5, 1] = GlobalVariables.PartNumber;//配料号
                oSheet.Cells[5, 2] = GlobalVariables.FilterType;
                oSheet.Cells[5, 3] = GlobalVariables.SerialNumber;//序列号
                oSheet.Cells[5, 4] = GlobalVariables.Today;
                oSheet.Cells[5, 5] = "001";

                oSheet.Cells[8, 1] = Filter.Length.ToString() + "X" + Filter.Width.ToString() + "X" + Filter.Height.ToString();
                oSheet.Cells[8, 2] = GlobalVariables.DesiredFlowrate.ToString();
                oSheet.Cells[8, 3] = Filter.Type;

                oSheet.Cells[13, 1] = "";//气压
                oSheet.Cells[13, 2] = KIMOSensors.CurrentOutT.ToString();//温度
                oSheet.Cells[13, 3] = KIMOSensors.CurrentOutRH.ToString();//相对湿度

                ushort row = 18, col = 1;

                foreach (DustFeedingDS fpData in DataTable.FPData)
                {
                    oSheet.Cells[row, col] = fpData.q_v.ToString("N1");
                    oSheet.Cells[row, col + 1] = fpData.delta_p.ToString();
                    oSheet.Cells[row, col + 2] = KIMOSensors.CurrentOutT.ToString();
                    oSheet.Cells[row, col + 3] = KIMOSensors.CurrentOutRH.ToString();

                    row++;
                }
            }
            else
            {
                MessageBox.Show("对不起，现在还没数据！");
            }

        }

        private void FPReportManualToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Thread GenerateFPReportManualThread = new Thread(GenerateFPReportManual);
            GenerateFPReportManualThread.Start();
        }

        private void GenerateFPReportManual()
        {
            if (DataTable.FPData_manual.Count > 0)
            {
                //variable for Excel Operation
                Excel.Application oXL;
                Excel._Workbook oWB;
                Excel._Worksheet oSheet;
                //Excel.Range oRng;

                oXL = new Excel.Application();
                oXL.Visible = true;

                string strFileName = "";

                if (GlobalVariables.ReportLanguage == "CHINESE")
                {
                    strFileName = GlobalVariables.RootDirectory + "doc\\风量阻力试验报告(手动)-EN1822.xls";
                }
                else if (GlobalVariables.ReportLanguage == "ENGLISH")
                {
                    strFileName = GlobalVariables.RootDirectory + "doc\\FPReport_EN.xls";
                }

                oWB =
                    oXL.Workbooks.Open(strFileName,
                    Type.Missing, Type.Missing, Type.Missing, Type.Missing,
                    Type.Missing, Type.Missing, Type.Missing, Type.Missing,
                    Type.Missing, Type.Missing, Type.Missing, Type.Missing,
                    Type.Missing, Type.Missing);

                ((Excel.Worksheet)oXL.ActiveWorkbook.Sheets[1]).Select(Type.Missing);

                oSheet = (Excel.Worksheet)oWB.Sheets[1];

                //string strTime = DateTime.Now.ToString();全局变量里有试试看能不能直接用
                oSheet.Cells[5, 1] = GlobalVariables.PartNumber;//配料号
                oSheet.Cells[5, 2] = GlobalVariables.FilterType;
                oSheet.Cells[5, 3] = GlobalVariables.SerialNumber;//序列号
                oSheet.Cells[5, 4] = GlobalVariables.Today;
                oSheet.Cells[5, 5] = "001";

                oSheet.Cells[8, 1] = Filter.Length.ToString() + "X" + Filter.Width.ToString() + "X" + Filter.Height.ToString();
                oSheet.Cells[8, 2] = GlobalVariables.DesiredFlowrate.ToString();
                oSheet.Cells[8, 3] = Filter.Type;

                oSheet.Cells[13, 1] = "";//气压
                oSheet.Cells[13, 2] = KIMOSensors.CurrentOutT.ToString();//温度
                oSheet.Cells[13, 3] = KIMOSensors.CurrentOutRH.ToString();//相对湿度

                ushort row = 18, col = 1;

                foreach (DustFeedingDS fpData in DataTable.FPData_manual)
                {
                    oSheet.Cells[row, col] = fpData.q_v.ToString();
                    oSheet.Cells[row, col + 1] = fpData.delta_p.ToString();
                    oSheet.Cells[row, col + 2] = KIMOSensors.CurrentOutT.ToString();
                    oSheet.Cells[row, col + 3] = KIMOSensors.CurrentOutRH.ToString();

                    row++;
                }
            }
            else
            {
                MessageBox.Show("对不起，现在还没数据！");
            }
        }

        private void generalInformationToolStripMenuItem_Click(object sender, EventArgs e)
        {
            GeneralInfoInputForm generalInfoInputForm = new GeneralInfoInputForm();
            generalInfoInputForm.ShowDialog();
        }

        private void FactoryNameSettingToolStripMenuItem_Click(object sender, EventArgs e)
        {
            FactoryNameSettingForm factoryNameSettingForm = new FactoryNameSettingForm();
            factoryNameSettingForm.ShowDialog();
        }

        private void adjustFlowrateToolStripMenuItem_Click(object sender, EventArgs e)
        {
            FlowrateAdjustingForm flowrateAdjustingForm = new FlowrateAdjustingForm();
            flowrateAdjustingForm.ShowDialog();
        }

        private void fPToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                FPForm fpForm = new FPForm();
                DialogResult dr = fpForm.ShowDialog();

                if (dr == DialogResult.OK)
                {
                    GlobalVariables.FP_cnt = 1;
                    GlobalVariables.DesiredFlowrate = (GlobalVariables.FP_cnt + 1) / 4.0 * GlobalVariables.RatedFlowrate - GlobalVariables.FlowrateRevision;

                    DataTable.FPData.Clear();

                    if (!FanDriver.IsRun)
                    {
                        FanDriver.Startup();
                    }

                    GlobalVariables.FlowrateAdjustingMode = Constant.FP_MODE;
                    FanDriver.AdjustFlowrateTimer.Enabled = true;

                    GlobalVariables.ApplicationCurrentStatus = "风量-阻力试验...";

                }
            }
            catch (System.Exception ex)
            {
                GlobalVariables.log.Error("Error in F-P experiment..." + ex.ToString());
            }
        }

        private void ACS510PictureBox_Click(object sender, EventArgs e)
        {
            ACS510Form ABBACS510Form = new ACS510Form();

            //变频器控制只可弹出一个;
            ABBACS510Form.ShowDialog();
        }

        private void CurrentFPDataRecord_ManualBtn_Click(object sender, EventArgs e)
        {
            DustFeedingDS fpData = new DustFeedingDS();

            fpData.Date = GlobalVariables.Today;

            fpData.q_v = 3669.1521 * GlobalVariables.NozzleDiameter * GlobalVariables.NozzleDiameter * Math.Sqrt(KIMOSensors.CurrentNozzleP) + GlobalVariables.FlowrateRevision;
            fpData.delta_p = KIMOSensors.CurrentFilterP;

            fpData.t_f = KIMOSensors.CurrentOutT;
            fpData.RH = KIMOSensors.CurrentOutRH;


            DataTable.FPData_manual.Add(fpData);

            MessageBox.Show("当前风量阻力已记录\n风量 ： " + fpData.q_v.ToString("N0") + "\n阻力 ： " + fpData.delta_p.ToString("N1") + "\n");  
        }

        private void FPDataClear_ManualBtn_Click(object sender, EventArgs e)
        {
            DialogResult dr = MessageBox.Show("确定要清除所有记录的风量阻力值？", "清除风量阻力记录", MessageBoxButtons.YesNo);

            if (dr == DialogResult.Yes)
            {
                DataTable.FPData_manual.Clear();
            }
        }

        private void FlowrateReviseBtn_Click(object sender, EventArgs e)
        {
            FlowrateRevisionForm flowrateRevisionForm = new FlowrateRevisionForm();
            flowrateRevisionForm.ShowDialog();
        }

        private void SaveScanningDataToolStripMenuItem_Click(object sender, EventArgs e)
        {
            string strDown = "";
            string strUp = "";

            string strTime = DateTime.Now.Hour.ToString() + "H" + DateTime.Now.Minute.ToString() + "M" + DateTime.Now.Second.ToString() + "S";

            MessageBox.Show("FaceScanningYBlockNumber = " + FaceScanningYBlockNumber.ToString() + "\n" + "FaceScanningXBlockNumber = " + FaceScanningXBlockNumber.ToString());

            for (uint i = 0; i < FaceScanningYBlockNumber; i++)
            {
                for (uint j = 0; j < FaceScanningXBlockNumber; j++)
                {
                    strDown += DownScanningData[i, j].RawPN + "\t";
                    strUp += UpScanningData[i, j].RawPN + "\t";
                }

                strDown += "\n";
                strUp += "\n";
            }

            File.WriteAllText("DownScanningData" + strTime + ".dat", strDown);

            File.WriteAllText("UpScanningData" + strTime + ".dat", strUp);


            string str = "";
            for (uint i = 0; i < FaceScanningYBlockNumber; i++)
            {
                for (uint j = 0; j < FaceScanningXBlockNumber; j++)
                {
                    str += DownScanningData[i, j].column.ToString() + "\t" + DownScanningData[i, j].row.ToString() + "\t" + DownScanningData[i, j].RawPN + "\n";
                }
            }

            File.WriteAllText("Data" + strTime + ".txt", str);     
        }
    }
}